Skip to content

Commit

Permalink
argocd_application: introduce wait parameter (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
oboukili authored Jan 28, 2021
1 parent 9503821 commit 603dc2c
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ For example if you use Docker as your local container runtime:
docker pull argoproj/argocd:v1.8.3
docker pull ghcr.io/dexidp/dex:v2.27.0
docker pull redis:5.0.10-alpine
docker pull banzaicloud/vault-operator:1.3.3
```
#### Troubleshooting during local development
Expand Down
65 changes: 63 additions & 2 deletions argocd/resource_argocd_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
applicationClient "github.com/argoproj/argo-cd/pkg/apiclient/application"
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"strings"
"time"
Expand All @@ -23,6 +25,17 @@ func resourceArgoCDApplication() *schema.Resource {
Schema: map[string]*schema.Schema{
"metadata": metadataSchema("applications.argoproj.io"),
"spec": applicationSpecSchema(),
"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.",
Optional: true,
Default: false,
},
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(5 * time.Minute),
},
}
}
Expand Down Expand Up @@ -78,6 +91,23 @@ func resourceArgoCDApplicationCreate(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("something went wrong during application creation")
}
d.SetId(app.Name)
if wait, ok := d.GetOk("wait"); ok && wait.(bool) {
return resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
a, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
Name: &app.Name,
})
if err != nil {
return resource.NonRetryableError(fmt.Errorf("error while waiting for application %s to be synced and healthy: %s", app.Name, err))
}
if a.Status.Health.Status != health.HealthStatusHealthy {
return resource.RetryableError(fmt.Errorf("expected application health status to be healthy but was %s", a.Status.Health.Status))
}
if a.Status.Sync.Status != application.SyncStatusCodeSynced {
return resource.RetryableError(fmt.Errorf("expected application sync status to be synced but was %s", a.Status.Sync.Status))
}
return resource.NonRetryableError(resourceArgoCDApplicationRead(d, meta))
})
}
return resourceArgoCDApplicationRead(d, meta)
}

Expand All @@ -87,8 +117,7 @@ func resourceArgoCDApplicationRead(d *schema.ResourceData, meta interface{}) err
appName := d.Id()
app, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
Name: &appName,
},
)
})
if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
Expand Down Expand Up @@ -141,6 +170,23 @@ func resourceArgoCDApplicationUpdate(d *schema.ResourceData, meta interface{}) e
if err != nil {
return err
}
if wait, _ok := d.GetOk("wait"); _ok && wait.(bool) {
return resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
a, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
Name: &app.Name,
})
if err != nil {
return resource.NonRetryableError(fmt.Errorf("error while waiting for application %s to be synced and healthy: %s", app.Name, err))
}
if a.Status.Health.Status != health.HealthStatusHealthy {
return resource.RetryableError(fmt.Errorf("expected application health status to be healthy but was %s", a.Status.Health.Status))
}
if a.Status.Sync.Status != application.SyncStatusCodeSynced {
return resource.RetryableError(fmt.Errorf("expected application sync status to be synced but was %s", a.Status.Sync.Status))
}
return resource.NonRetryableError(resourceArgoCDApplicationRead(d, meta))
})
}
}
return resourceArgoCDApplicationRead(d, meta)
}
Expand All @@ -153,6 +199,21 @@ func resourceArgoCDApplicationDelete(d *schema.ResourceData, meta interface{}) e
if err != nil && !strings.Contains(err.Error(), "NotFound") {
return err
}
if wait, ok := d.GetOk("wait"); ok && wait.(bool) {
return resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
_, err = c.Get(context.Background(), &applicationClient.ApplicationQuery{
Name: &appName,
})
if err == nil {
return resource.RetryableError(fmt.Errorf("application %s is still present", appName))
}
if !strings.Contains(err.Error(), "NotFound") {
return resource.NonRetryableError(err)
}
d.SetId("")
return nil
})
}
d.SetId("")
return nil
}
51 changes: 51 additions & 0 deletions argocd/resource_argocd_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ ingress:
),
),
},
{
Config: testAccArgoCDApplicationSimpleWait(commonName),
ExpectNonEmptyPlan: true,
Check: resource.TestCheckResourceAttr(
"argocd_application.simple",
"wait",
"true",
),
},
{
Config: testAccArgoCDApplicationHelm(
acctest.RandomWithPrefix("test-acc"),
Expand Down Expand Up @@ -221,6 +230,48 @@ resource "argocd_application" "simple" {
`, name)
}

func testAccArgoCDApplicationSimpleWait(name string) string {
return fmt.Sprintf(`
resource "argocd_application" "simple" {
metadata {
name = "%s"
namespace = "argocd"
labels = {
acceptance = "true"
}
annotations = {
"this.is.a.really.long.nested.key" = "yes, really!"
}
}
spec {
source {
repo_url = "https://kubernetes-charts.banzaicloud.com"
chart = "vault-operator"
target_revision = "1.3.3"
helm {
parameter {
name = "image.tag"
value = "1.3.3"
}
release_name = "testing"
}
}
sync_policy {
automated = {
prune = true
self_heal = true
}
}
destination {
server = "https://kubernetes.default.svc"
namespace = "default"
}
}
wait = true
}
`, name)
}

func testAccArgoCDApplicationHelm(name, helmValues string) string {
return fmt.Sprintf(`
resource "argocd_application" "helm" {
Expand Down
1 change: 0 additions & 1 deletion argocd/schema_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ func applicationSpecSchema() *schema.Schema {
},
"retry": {
Type: schema.TypeList,
MinItems: 1,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Expand Down
3 changes: 3 additions & 0 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ resource "argocd_application" "helm" {
}
}
wait = true
spec {
source {
repo_url = "https://some.chart.repo.io"
Expand Down Expand Up @@ -120,6 +122,7 @@ EOT

* `metadata` - (Required) Standard Kubernetes API service's metadata. For more info see the [Kubernetes reference](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata).
* `spec` - (Required) The application specification, the nested attributes are documented below.
* `wait` - (Optional) boolean, wait for application to be synced and healthy upon creation and updates, also waits for Kubernetes resources to be truly deleted upon deletion. Wait timeouts are controlled by Terraform Create, Update and Delete resource timeouts (all default to 5 minutes). Default is `false`.

The `metadata` block can have the following attributes:

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.15
require (
github.com/Masterminds/semver v1.5.0
github.com/argoproj/argo-cd v1.8.3
github.com/argoproj/gitops-engine v0.2.1
github.com/argoproj/pkg v0.2.0
github.com/cristalhq/jwt/v3 v3.0.8
github.com/golang/protobuf v1.4.3
Expand Down
1 change: 1 addition & 0 deletions scripts/testacc_prepare_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ kubectl get services --all-namespaces -o wide
echo '--- Load already available container images from local registry into Kind (local development only)'
kind load docker-image redis:5.0.10-alpine --name argocd
kind load docker-image ghcr.io/dexidp/dex:v2.27.0 --name argocd
kind load docker-image banzaicloud/vault-operator:1.3.3 --name argocd
kind load docker-image argoproj/argocd:${ARGOCD_VERSION:-v1.8.3} --name argocd

echo '--- Install ArgoCD ${ARGOCD_VERSION:-v1.8.3}\n\n'
Expand Down

0 comments on commit 603dc2c

Please sign in to comment.