Skip to content

Commit

Permalink
Add compatiblity for "source_namespaces" introduced in ArgoCD v2.5.0 (#…
Browse files Browse the repository at this point in the history
…212)

* add source_namespaces

* update pinned k8s.io versions

* set: github.com/argoproj/gitops-engine => github.com/argoproj/gitops-engine v0.7.1-0.20220804190909-2bc3fef13e07

* Add reference to issue

* Add additional tests, update test environment, update test instructions in README

* remove source_namespaces from schemas V0 and V1

* Add "AppNamespace" find to application queries

* Prepare an env that can test "application namespaces" feature

* Add "application namespaces" acceptance tests

* Set argocd_version to 2.5.0 in automated tests

* Run "Application" acceptance tests against "test" AppProject

* bump go version to 1.19

* Bump go version on release pipeline

* Add older versions back into acceptane testing pipeline

* separate testdata for pre and post v2.5.0

* make a dedicated "testAccArgoCDProjectSimpleWithSourceNamespaces" test

* Fixes to argocd project changes

- Mark source_namespaces as Optional
- Add feature/version constraint 
- Add test for project with source namespaces

* Revert "separate testdata for pre and post v2.5.0"

This reverts commit 0119016.

* Revert "Run "Application" acceptance tests against "test" AppProject"

This reverts commit 1a011ff.

* Revert "Add "application namespaces" acceptance tests"

This reverts commit 5858bfa.

* Revert changes to `token_resource`

* Patch default installation to support deployment of apps to specific namespace

* Format application + tests

* Add test deploying application to custom namespace

* Use composite ID for ArgoCD applications

To be able to import existing resources in custom namespaces we need to store the namespace as part of the ID on the ArgoCD application resource.

* Format test resource

* Fix typo in error message

Co-authored-by: Olivier Boukili <[email protected]>

* Use `TypeSet ` instead of `List` for project `source_namespaces`

* Simplify `applicationSpecSchemaV3`

Schema for `application.spec` has not changed.

* Ensure test resources are created in correct order

Co-authored-by: Brian Fox <[email protected]>
Co-authored-by: Olivier Boukili <[email protected]>
  • Loading branch information
3 people authored Dec 12, 2022
1 parent 47115ea commit bf3c417
Show file tree
Hide file tree
Showing 20 changed files with 584 additions and 292 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.19

- name: Import GPG key
id: import_gpg
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
strategy:
fail-fast: false
matrix:
argocd_version: ["v2.4.12", "v2.3.9"]
argocd_version: ["v2.5.0", "v2.4.12", "v2.3.9"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v1
with:
go-version: 1.17
go-version: 1.19
id: go
- name: Restore Go cache
uses: actions/cache@v1
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,10 @@ make testacc_clean_env
For example if you use Docker as your local container runtime:
```shell
docker pull argoproj/argocd:v1.8.3
docker pull quay.io/argoproj/argocd:v2.5.0
docker pull ghcr.io/dexidp/dex:v2.27.0
docker pull redis:6.2.4-alpine
docker pull bitnami/redis:6.2.5
docker pull alpine:3
```
#### Troubleshooting during local development
Expand Down
2 changes: 2 additions & 0 deletions argocd/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
featureRepositoryCertificates
featureApplicationHelmSkipCrds
featureExecLogsPolicy
featureProjectSourceNamespaces
)

var featureVersionConstraintsMap = map[int]*semver.Version{
Expand All @@ -41,6 +42,7 @@ var featureVersionConstraintsMap = map[int]*semver.Version{
featureRepositoryCertificates: semver.MustParse("1.2.0"),
featureApplicationHelmSkipCrds: semver.MustParse("2.3.0"),
featureExecLogsPolicy: semver.MustParse("2.4.0"),
featureProjectSourceNamespaces: semver.MustParse("2.5.0"),
}

type ServerInterface struct {
Expand Down
52 changes: 37 additions & 15 deletions argocd/resource_argocd_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func resourceArgoCDApplication() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"metadata": metadataSchema("applications.argoproj.io"),
"spec": applicationSpecSchemaV2(),
"spec": applicationSpecSchemaV3(),
"wait": {
Type: schema.TypeBool,
Description: "Upon application creation or update, wait for application health/sync status to be healthy/Synced, upon application deletion, wait for application to be removed, when set to true.",
Expand All @@ -41,7 +41,7 @@ func resourceArgoCDApplication() *schema.Resource {
Default: true,
},
},
SchemaVersion: 2,
SchemaVersion: 3,
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceArgoCDApplicationV0().CoreConfigSchema().ImpliedType(),
Expand All @@ -53,6 +53,11 @@ func resourceArgoCDApplication() *schema.Resource {
Upgrade: resourceArgoCDApplicationStateUpgradeV1,
Version: 1,
},
{
Type: resourceArgoCDApplicationV2().CoreConfigSchema().ImpliedType(),
Upgrade: resourceArgoCDApplicationStateUpgradeV2,
Version: 2,
},
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(5 * time.Minute),
Expand All @@ -79,7 +84,8 @@ func resourceArgoCDApplicationCreate(ctx context.Context, d *schema.ResourceData
}
c := *server.ApplicationClient
app, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &objectMeta.Name,
Name: &objectMeta.Name,
AppNamespace: &objectMeta.Namespace,
})
if err != nil && !strings.Contains(err.Error(), "NotFound") {
return []diag.Diagnostic{
Expand Down Expand Up @@ -205,11 +211,14 @@ func resourceArgoCDApplicationCreate(ctx context.Context, d *schema.ResourceData
},
}
}
d.SetId(app.Name)

d.SetId(fmt.Sprintf("%s:%s", app.Name, objectMeta.Namespace))

if wait, ok := d.GetOk("wait"); ok && wait.(bool) {
err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
a, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &app.Name,
Name: &app.Name,
AppNamespace: &app.Namespace,
})
if err != nil {
return resource.NonRetryableError(fmt.Errorf("error while waiting for application %s to be synced and healthy: %s", app.Name, err))
Expand Down Expand Up @@ -246,10 +255,16 @@ func resourceArgoCDApplicationRead(ctx context.Context, d *schema.ResourceData,
},
}
}

c := *server.ApplicationClient
appName := d.Id()

ids := strings.Split(d.Id(), ":")
appName := ids[0]
namespace := ids[1]

app, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &appName,
Name: &appName,
AppNamespace: &namespace,
})
if err != nil {
if strings.Contains(err.Error(), "NotFound") {
Expand Down Expand Up @@ -278,7 +293,9 @@ func resourceArgoCDApplicationRead(ctx context.Context, d *schema.ResourceData,
}

func resourceArgoCDApplicationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
appName := d.Id()
ids := strings.Split(d.Id(), ":")
appName := ids[0]
namespace := ids[1]
if ok := d.HasChanges("metadata", "spec"); ok {
objectMeta, spec, diags := expandApplication(d)
if diags != nil {
Expand Down Expand Up @@ -384,9 +401,9 @@ func resourceArgoCDApplicationUpdate(ctx context.Context, d *schema.ResourceData
}
}
}

app, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &appName,
Name: &appName,
AppNamespace: &namespace,
})
if app != nil {
// Kubernetes API requires providing the up-to-date correct ResourceVersion for updates
Expand All @@ -406,7 +423,8 @@ func resourceArgoCDApplicationUpdate(ctx context.Context, d *schema.ResourceData
if wait, _ok := d.GetOk("wait"); _ok && wait.(bool) {
err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
a, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &app.Name,
Name: &app.Name,
AppNamespace: &app.Namespace,
})
if err != nil {
return resource.NonRetryableError(fmt.Errorf("error while waiting for application %s to be synced and healthy: %s", app.Name, err))
Expand Down Expand Up @@ -445,11 +463,14 @@ func resourceArgoCDApplicationDelete(ctx context.Context, d *schema.ResourceData
}
}
c := *server.ApplicationClient
appName := d.Id()
ids := strings.Split(d.Id(), ":")
appName := ids[0]
namespace := ids[1]
cascade := d.Get("cascade").(bool)
_, err := c.Delete(ctx, &applicationClient.ApplicationDeleteRequest{
Name: &appName,
Cascade: &cascade,
Name: &appName,
Cascade: &cascade,
AppNamespace: &namespace,
})
if err != nil && !strings.Contains(err.Error(), "NotFound") {
return []diag.Diagnostic{
Expand All @@ -463,7 +484,8 @@ func resourceArgoCDApplicationDelete(ctx context.Context, d *schema.ResourceData
if wait, ok := d.GetOk("wait"); ok && wait.(bool) {
err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
_, err := c.Get(ctx, &applicationClient.ApplicationQuery{
Name: &appName,
Name: &appName,
AppNamespace: &namespace,
})
if err == nil {
return resource.RetryableError(fmt.Errorf("application %s is still present", appName))
Expand Down
107 changes: 98 additions & 9 deletions argocd/resource_argocd_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ func TestAccArgoCDApplication_NoSyncPolicyBlock(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_Recurse(t *testing.T) {
Expand Down Expand Up @@ -419,7 +420,8 @@ func TestAccArgoCDApplication_Recurse(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_EmptySyncPolicyBlock(t *testing.T) {
Expand All @@ -444,7 +446,8 @@ func TestAccArgoCDApplication_EmptySyncPolicyBlock(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_NoAutomatedBlock(t *testing.T) {
Expand All @@ -469,7 +472,8 @@ func TestAccArgoCDApplication_NoAutomatedBlock(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_EmptyAutomatedBlock(t *testing.T) {
Expand All @@ -490,7 +494,8 @@ func TestAccArgoCDApplication_EmptyAutomatedBlock(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_OptionalPath(t *testing.T) {
Expand Down Expand Up @@ -542,7 +547,8 @@ func TestAccArgoCDApplication_OptionalPath(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_Info(t *testing.T) {
Expand Down Expand Up @@ -657,7 +663,8 @@ func TestAccArgoCDApplication_Info(t *testing.T) {
),
),
},
}})
},
})
}

func TestProvider_headers(t *testing.T) {
Expand Down Expand Up @@ -746,7 +753,8 @@ func TestAccArgoCDApplication_SkipCrds_NotSupported_On_OlderVersions(t *testing.
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_SkipCrds(t *testing.T) {
Expand Down Expand Up @@ -798,7 +806,34 @@ func TestAccArgoCDApplication_SkipCrds(t *testing.T) {
),
),
},
}})
},
})
}

func TestAccArgoCDApplication_CustomNamespace(t *testing.T) {
name := acctest.RandomWithPrefix("test-acc-custom-namespace")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckFeatureSupported(t, featureProjectSourceNamespaces) },
ProviderFactories: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccArgoCDApplicationCustomNamespace(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(
"argocd_application.simple",
"metadata.0.uid",
),
),
},
{
ResourceName: "argocd_application.simple",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"wait", "cascade"},
},
},
})
}

func testAccArgoCDApplicationSimple(name string) string {
Expand Down Expand Up @@ -1659,6 +1694,60 @@ resource "argocd_application" "crds" {
`, name)
}

func testAccArgoCDApplicationCustomNamespace(name string) string {
return fmt.Sprintf(`
resource "argocd_project" "simple" {
metadata {
name = "%[1]s"
namespace = "argocd"
}
spec {
description = "project with source namespace"
source_repos = ["*"]
source_namespaces = ["mynamespace-1"]
destination {
server = "https://kubernetes.default.svc"
namespace = "default"
}
}
}
resource "argocd_application" "simple" {
metadata {
name = "%[1]s"
namespace = "mynamespace-1"
}
spec {
project = argocd_project.simple.metadata[0].name
source {
repo_url = "https://charts.bitnami.com/bitnami"
chart = "redis"
target_revision = "16.9.11"
helm {
parameter {
name = "image.tag"
value = "6.2.5"
}
parameter {
name = "architecture"
value = "standalone"
}
release_name = "testing"
}
}
destination {
server = "https://kubernetes.default.svc"
namespace = "default"
}
}
}
`, name)
}

func testAccSkipFeatureIgnoreDiffJQPathExpressions() (bool, error) {
p, _ := testAccProviders["argocd"]()
_ = p.Configure(context.Background(), &terraform.ResourceConfig{})
Expand Down
24 changes: 24 additions & 0 deletions argocd/resource_argocd_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ func resourceArgoCDProjectCreate(ctx context.Context, d *schema.ResourceData, me
}
}

featureProjectSourceNamespacesSupported, err := server.isFeatureSupported(featureProjectSourceNamespaces)
if err != nil {
return []diag.Diagnostic{
{
Severity: diag.Error,
Summary: "feature not supported",
Detail: err.Error(),
},
}
}
if !featureProjectSourceNamespacesSupported {
_, sourceNamespacesOk := d.GetOk("spec.0.source_namespaces")
if sourceNamespacesOk {
return []diag.Diagnostic{
{
Severity: diag.Error,
Summary: fmt.Sprintf(
"project source_namespaces is only supported from ArgoCD %s onwards",
featureVersionConstraintsMap[featureProjectSourceNamespaces].String()),
},
}
}
}

tokenMutexProjectMap[projectName].Lock()
p, err = c.Create(ctx, &projectClient.ProjectCreateRequest{
Project: &application.AppProject{
Expand Down
Loading

0 comments on commit bf3c417

Please sign in to comment.