Skip to content

Commit c632739

Browse files
authored
Adds argocd_application resource (#16)
added argocd_application resource upgraded api clients to argocd 1.6.1 added argocd 1.6.1 to acceptance testing,
1 parent 6e11f1f commit c632739

16 files changed

+2284
-327
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
fail-fast: false
1616
matrix:
17-
argocd_version: ["v1.5.4", "v1.4.3"]
17+
argocd_version: ["v1.6.1", "v1.5.8", "v1.4.3"]
1818
steps:
1919
- uses: actions/checkout@v2
2020
- uses: actions/setup-go@v1

README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,109 @@ resource "argocd_project_token" "secret" {
139139
expires_in = "1h"
140140
renew_before = "30m"
141141
}
142+
143+
resource "argocd_application" "kustomize" {
144+
metadata {
145+
name = "kustomize-app"
146+
namespace = "argocd"
147+
labels = {
148+
test = "true"
149+
}
150+
}
151+
152+
spec {
153+
project = argocd_project.myproject.metadata.0.name
154+
155+
source {
156+
repo_url = "https://github.com/kubernetes-sigs/kustomize"
157+
path = "examples/helloWorld"
158+
target_revision = "master"
159+
kustomize {
160+
name_prefix = "foo-"
161+
name_suffix = "-bar"
162+
images = [
163+
"hashicorp/terraform:light",
164+
]
165+
common_labels = {
166+
"this.is.a.common" = "la-bel"
167+
"another.io/one" = "true"
168+
}
169+
}
170+
}
171+
172+
destination {
173+
server = "https://kubernetes.default.svc"
174+
namespace = "foo"
175+
}
176+
177+
sync_policy {
178+
automated = {
179+
prune = true
180+
self_heal = true
181+
}
182+
# Only available from ArgoCD 1.5.0 onwards
183+
sync_options = ["Validate=false"]
184+
}
185+
186+
ignore_difference {
187+
group = "apps"
188+
kind = "Deployment"
189+
json_pointers = ["/spec/replicas"]
190+
}
191+
192+
ignore_difference {
193+
group = "apps"
194+
kind = "StatefulSet"
195+
name = "someStatefulSet"
196+
json_pointers = [
197+
"/spec/replicas",
198+
"/spec/template/spec/metadata/labels/bar",
199+
]
200+
}
201+
}
202+
}
203+
204+
resource "argocd_application" "helm" {
205+
metadata {
206+
name = "helm-app"
207+
namespace = "argocd"
208+
labels = {
209+
test = "true"
210+
}
211+
}
212+
213+
spec {
214+
source {
215+
repo_url = "https://some.chart.repo.io"
216+
chart = "mychart"
217+
target_revision = "1.2.3"
218+
helm {
219+
parameter {
220+
name = "image.tag"
221+
value = "1.2.3"
222+
}
223+
parameter {
224+
name = "someotherparameter"
225+
value = "true"
226+
}
227+
value_files = ["values-test.yml"]
228+
values = <<EOT
229+
someparameter:
230+
enabled: true
231+
someArray:
232+
- foo
233+
- bar
234+
EOT
235+
release_name = "testing"
236+
}
237+
}
238+
239+
destination {
240+
server = "https://kubernetes.default.svc"
241+
namespace = "default"
242+
}
243+
}
244+
}
142245
```
143246

144247
---
@@ -172,3 +275,9 @@ make testacc_prepare_env
172275
make testacc
173276
make testacc_clean_env
174277
```
278+
279+
---
280+
281+
## Credits
282+
283+
Thanks to [Keplr](https://www.welcometothejungle.com/fr/companies/keplr) for allowing me to contribute to this side-project of mine during paid work hours.

argocd/features.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,26 @@ import (
44
"fmt"
55
"github.com/Masterminds/semver"
66
"github.com/argoproj/argo-cd/pkg/apiclient"
7+
"github.com/argoproj/argo-cd/pkg/apiclient/application"
78
"github.com/argoproj/argo-cd/pkg/apiclient/project"
89
"github.com/argoproj/argo-cd/pkg/apiclient/version"
910
)
1011

1112
const (
12-
featureTokenIDs = iota
13+
featureApplicationLevelSyncOptions = iota
14+
featureTokenIDs
1315
)
1416

1517
var (
1618
featureVersionConstraintsMap = map[int]*semver.Version{
17-
featureTokenIDs: semver.MustParse("1.5.3"),
19+
featureApplicationLevelSyncOptions: semver.MustParse("1.5.0"),
20+
featureTokenIDs: semver.MustParse("1.5.3"),
1821
}
1922
)
2023

2124
type ServerInterface struct {
2225
ApiClient apiclient.Client
26+
ApplicationClient application.ApplicationServiceClient
2327
ProjectClient project.ProjectServiceClient
2428
ServerVersion *semver.Version
2529
ServerVersionMessage *version.VersionMessage

argocd/provider.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import (
55
"fmt"
66
"github.com/Masterminds/semver"
77
"github.com/argoproj/argo-cd/pkg/apiclient"
8+
"github.com/argoproj/argo-cd/pkg/apiclient/application"
89
"github.com/argoproj/argo-cd/pkg/apiclient/project"
910
"github.com/argoproj/argo-cd/pkg/apiclient/session"
10-
"github.com/argoproj/argo-cd/util"
11+
util "github.com/argoproj/gitops-engine/pkg/utils/io"
1112
"github.com/golang/protobuf/ptypes/empty"
1213
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
1314
"github.com/hashicorp/terraform-plugin-sdk/terraform"
@@ -99,6 +100,7 @@ func Provider(doneCh chan bool) terraform.ResourceProvider {
99100
},
100101

101102
ResourcesMap: map[string]*schema.Resource{
103+
"argocd_application": resourceArgoCDApplication(),
102104
"argocd_project": resourceArgoCDProject(),
103105
"argocd_project_token": resourceArgoCDProjectToken(),
104106
},
@@ -112,17 +114,26 @@ func Provider(doneCh chan bool) terraform.ResourceProvider {
112114
return nil, err
113115
}
114116

117+
acCloser, applicationClient, err := apiClient.NewApplicationClient()
118+
if err != nil {
119+
return nil, err
120+
}
115121
// Clients connection pooling, close when the provider execution ends
116122
go func(done chan bool) {
117123
<-done
118124
util.Close(pcCloser)
125+
util.Close(acCloser)
119126
}(doneCh)
120-
return initServerInterface(apiClient, projectClient)
127+
return initServerInterface(apiClient, projectClient, applicationClient)
121128
},
122129
}
123130
}
124131

125-
func initServerInterface(apiClient apiclient.Client, projectClient project.ProjectServiceClient) (interface{}, error) {
132+
func initServerInterface(
133+
apiClient apiclient.Client,
134+
projectClient project.ProjectServiceClient,
135+
applicationClient application.ApplicationServiceClient,
136+
) (interface{}, error) {
126137
acCloser, versionClient, err := apiClient.NewVersionClient()
127138
if err != nil {
128139
return nil, err
@@ -143,6 +154,7 @@ func initServerInterface(apiClient apiclient.Client, projectClient project.Proje
143154

144155
return ServerInterface{
145156
apiClient,
157+
applicationClient,
146158
projectClient,
147159
serverVersion,
148160
serverVersionMessage}, err
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package argocd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
applicationClient "github.com/argoproj/argo-cd/pkg/apiclient/application"
7+
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
9+
"strings"
10+
"time"
11+
)
12+
13+
func resourceArgoCDApplication() *schema.Resource {
14+
return &schema.Resource{
15+
Create: resourceArgoCDApplicationCreate,
16+
Read: resourceArgoCDApplicationRead,
17+
Update: resourceArgoCDApplicationUpdate,
18+
Delete: resourceArgoCDApplicationDelete,
19+
// TODO: add importer acceptance tests
20+
Importer: &schema.ResourceImporter{
21+
State: schema.ImportStatePassthrough,
22+
},
23+
Schema: map[string]*schema.Schema{
24+
"metadata": metadataSchema("applications.argoproj.io"),
25+
"spec": applicationSpecSchema(),
26+
},
27+
}
28+
}
29+
30+
func resourceArgoCDApplicationCreate(d *schema.ResourceData, meta interface{}) error {
31+
objectMeta, spec, err := expandApplication(d)
32+
if err != nil {
33+
return err
34+
}
35+
server := meta.(ServerInterface)
36+
c := server.ApplicationClient
37+
app, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
38+
Name: &objectMeta.Name,
39+
})
40+
if err != nil {
41+
switch strings.Contains(err.Error(), "NotFound") {
42+
case true:
43+
default:
44+
return err
45+
}
46+
}
47+
if app != nil {
48+
switch app.DeletionTimestamp {
49+
case nil:
50+
default:
51+
// Pre-existing app is still in Kubernetes soft deletion queue
52+
time.Sleep(time.Duration(*app.DeletionGracePeriodSeconds))
53+
}
54+
}
55+
56+
featureApplicationLevelSyncOptionsSupported, err := server.isFeatureSupported(featureApplicationLevelSyncOptions)
57+
if err != nil {
58+
return err
59+
}
60+
if !featureApplicationLevelSyncOptionsSupported &&
61+
spec.SyncPolicy != nil &&
62+
spec.SyncPolicy.SyncOptions != nil {
63+
return fmt.Errorf(
64+
"application-level sync_options is only supported from ArgoCD %s onwards",
65+
featureVersionConstraintsMap[featureApplicationLevelSyncOptions].String())
66+
}
67+
68+
app, err = c.Create(context.Background(), &applicationClient.ApplicationCreateRequest{
69+
Application: application.Application{
70+
ObjectMeta: objectMeta,
71+
Spec: spec,
72+
},
73+
})
74+
if err != nil {
75+
return err
76+
}
77+
if app == nil {
78+
return fmt.Errorf("something went wrong during application creation")
79+
}
80+
d.SetId(app.Name)
81+
return resourceArgoCDApplicationRead(d, meta)
82+
}
83+
84+
func resourceArgoCDApplicationRead(d *schema.ResourceData, meta interface{}) error {
85+
server := meta.(ServerInterface)
86+
c := server.ApplicationClient
87+
appName := d.Id()
88+
app, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
89+
Name: &appName,
90+
},
91+
)
92+
if err != nil {
93+
switch strings.Contains(err.Error(), "NotFound") {
94+
case true:
95+
d.SetId("")
96+
return nil
97+
default:
98+
return err
99+
}
100+
}
101+
err = flattenApplication(app, d)
102+
return err
103+
}
104+
105+
func resourceArgoCDApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
106+
appName := d.Id()
107+
108+
if ok := d.HasChanges("metadata", "spec"); ok {
109+
objectMeta, spec, err := expandApplication(d)
110+
if err != nil {
111+
return err
112+
}
113+
server := meta.(ServerInterface)
114+
c := server.ApplicationClient
115+
appRequest := &applicationClient.ApplicationUpdateRequest{
116+
Application: &application.Application{
117+
ObjectMeta: objectMeta,
118+
Spec: spec,
119+
}}
120+
121+
featureApplicationLevelSyncOptionsSupported, err := server.isFeatureSupported(featureApplicationLevelSyncOptions)
122+
if err != nil {
123+
return err
124+
}
125+
if !featureApplicationLevelSyncOptionsSupported &&
126+
spec.SyncPolicy != nil &&
127+
spec.SyncPolicy.SyncOptions != nil {
128+
return fmt.Errorf(
129+
"application-level sync_options is only supported from ArgoCD %s onwards",
130+
featureVersionConstraintsMap[featureApplicationLevelSyncOptions].String())
131+
}
132+
133+
app, err := c.Get(context.Background(), &applicationClient.ApplicationQuery{
134+
Name: &appName,
135+
})
136+
if app != nil {
137+
// Kubernetes API requires providing the up-to-date correct ResourceVersion for updates
138+
appRequest.Application.ResourceVersion = app.ResourceVersion
139+
}
140+
_, err = c.Update(context.Background(), appRequest)
141+
if err != nil {
142+
return err
143+
}
144+
}
145+
return resourceArgoCDApplicationRead(d, meta)
146+
}
147+
148+
func resourceArgoCDApplicationDelete(d *schema.ResourceData, meta interface{}) error {
149+
server := meta.(ServerInterface)
150+
c := server.ApplicationClient
151+
appName := d.Id()
152+
_, err := c.Delete(context.Background(), &applicationClient.ApplicationDeleteRequest{Name: &appName})
153+
if err != nil {
154+
return err
155+
}
156+
d.SetId("")
157+
return nil
158+
}

0 commit comments

Comments
 (0)