@@ -20,13 +20,18 @@ import (
20
20
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
21
21
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
22
22
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
23
- argoio "github.com/argoproj/argo-cd/v2/util/io"
24
23
"github.com/argoproj/gitops-engine/pkg/sync/hook"
25
24
"github.com/google/go-cmp/cmp"
26
25
log "github.com/sirupsen/logrus"
27
26
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28
27
)
29
28
29
+ type argoCdClients struct {
30
+ app application.ApplicationServiceClient
31
+ project projectpkg.ProjectServiceClient
32
+ setting settings.SettingsServiceClient
33
+ }
34
+
30
35
// DiffElement struct to store diff element details, this represents a single k8s object
31
36
type DiffElement struct {
32
37
ObjectGroup string
@@ -51,25 +56,25 @@ type DiffResult struct {
51
56
func generateArgocdAppDiff (ctx context.Context , app * argoappv1.Application , proj * argoappv1.AppProject , resources * application.ManagedResourcesResponse , argoSettings * settings.Settings , diffOptions * DifferenceOption ) (foundDiffs bool , diffElements []DiffElement , err error ) {
52
57
liveObjs , err := cmdutil .LiveObjects (resources .Items )
53
58
if err != nil {
54
- return false , nil , fmt .Errorf ("Failed to get live objects: %v " , err )
59
+ return false , nil , fmt .Errorf ("Failed to get live objects: %w " , err )
55
60
}
56
61
57
62
items := make ([]objKeyLiveTarget , 0 )
58
63
var unstructureds []* unstructured.Unstructured
59
64
for _ , mfst := range diffOptions .res .Manifests {
60
65
obj , err := argoappv1 .UnmarshalToUnstructured (mfst )
61
66
if err != nil {
62
- return false , nil , fmt .Errorf ("Failed to unmarshal manifest: %v " , err )
67
+ return false , nil , fmt .Errorf ("Failed to unmarshal manifest: %w " , err )
63
68
}
64
69
unstructureds = append (unstructureds , obj )
65
70
}
66
71
groupedObjs , err := groupObjsByKey (unstructureds , liveObjs , app .Spec .Destination .Namespace )
67
72
if err != nil {
68
- return false , nil , fmt .Errorf ("Failed to group objects by key: %v " , err )
73
+ return false , nil , fmt .Errorf ("Failed to group objects by key: %w " , err )
69
74
}
70
75
items , err = groupObjsForDiff (resources , groupedObjs , items , argoSettings , app .InstanceName (argoSettings .ControllerNamespace ), app .Spec .Destination .Namespace )
71
76
if err != nil {
72
- return false , nil , fmt .Errorf ("Failed to group objects for diff: %v " , err )
77
+ return false , nil , fmt .Errorf ("Failed to group objects for diff: %w " , err )
73
78
}
74
79
75
80
for _ , item := range items {
@@ -91,11 +96,11 @@ func generateArgocdAppDiff(ctx context.Context, app *argoappv1.Application, proj
91
96
WithNoCache ().
92
97
Build ()
93
98
if err != nil {
94
- return false , nil , fmt .Errorf ("Failed to build diff config: %v " , err )
99
+ return false , nil , fmt .Errorf ("Failed to build diff config: %w " , err )
95
100
}
96
101
diffRes , err := argodiff .StateDiff (item .live , item .target , diffConfig )
97
102
if err != nil {
98
- return false , nil , fmt .Errorf ("Failed to diff objects: %v " , err )
103
+ return false , nil , fmt .Errorf ("Failed to diff objects: %w " , err )
99
104
}
100
105
101
106
if diffRes .Modified || item .target == nil || item .live == nil {
@@ -111,7 +116,7 @@ func generateArgocdAppDiff(ctx context.Context, app *argoappv1.Application, proj
111
116
live = item .live
112
117
err = json .Unmarshal (diffRes .PredictedLive , target )
113
118
if err != nil {
114
- return false , nil , fmt .Errorf ("Failed to unmarshal predicted live object: %v " , err )
119
+ return false , nil , fmt .Errorf ("Failed to unmarshal predicted live object: %w " , err )
115
120
}
116
121
} else {
117
122
live = item .live
@@ -123,7 +128,7 @@ func generateArgocdAppDiff(ctx context.Context, app *argoappv1.Application, proj
123
128
124
129
diffElement .Diff , err = diffLiveVsTargetObject (live , target )
125
130
if err != nil {
126
- return false , nil , fmt .Errorf ("Failed to diff live objects: %v " , err )
131
+ return false , nil , fmt .Errorf ("Failed to diff live objects: %w " , err )
127
132
}
128
133
}
129
134
diffElements = append (diffElements , diffElement )
@@ -144,7 +149,7 @@ func getEnv(key, fallback string) string {
144
149
return fallback
145
150
}
146
151
147
- func createArgoCdClient () (apiclient. Client , error ) {
152
+ func createArgoCdClients () (ac argoCdClients , err error ) {
148
153
plaintext , _ := strconv .ParseBool (getEnv ("ARGOCD_PLAINTEXT" , "false" ))
149
154
insecure , _ := strconv .ParseBool (getEnv ("ARGOCD_INSECURE" , "false" ))
150
155
@@ -155,11 +160,26 @@ func createArgoCdClient() (apiclient.Client, error) {
155
160
Insecure : insecure ,
156
161
}
157
162
158
- clientset , err := apiclient .NewClient (opts )
163
+ client , err := apiclient .NewClient (opts )
164
+ if err != nil {
165
+ return ac , fmt .Errorf ("Error creating ArgoCD API client: %w" , err )
166
+ }
167
+
168
+ _ , ac .app , err = client .NewApplicationClient ()
169
+ if err != nil {
170
+ return ac , fmt .Errorf ("Error creating ArgoCD app client: %w" , err )
171
+ }
172
+
173
+ _ , ac .project , err = client .NewProjectClient ()
174
+ if err != nil {
175
+ return ac , fmt .Errorf ("Error creating ArgoCD project client: %w" , err )
176
+ }
177
+
178
+ _ , ac .setting , err = client .NewSettingsClient ()
159
179
if err != nil {
160
- return nil , fmt .Errorf ("Error creating ArgoCD API client: %v " , err )
180
+ return ac , fmt .Errorf ("Error creating ArgoCD settings client: %w " , err )
161
181
}
162
- return clientset , nil
182
+ return
163
183
}
164
184
165
185
// findArgocdAppBySHA1Label finds an ArgoCD application by the SHA1 label of the component path it's supposed to avoid performance issues with the "manifest-generate-paths" annotation method which requires pulling all ArgoCD applications(!) on every PR event.
@@ -178,7 +198,7 @@ func findArgocdAppBySHA1Label(ctx context.Context, componentPath string, repo st
178
198
}
179
199
foundApps , err := appClient .List (ctx , & appLabelQuery )
180
200
if err != nil {
181
- return nil , fmt .Errorf ("Error listing ArgoCD applications: %v " , err )
201
+ return nil , fmt .Errorf ("Error listing ArgoCD applications: %w " , err )
182
202
}
183
203
if len (foundApps .Items ) == 0 {
184
204
return nil , fmt .Errorf ("No ArgoCD application found for component path sha1 %s(repo %s), used this label selector: %s" , componentPathSha1 , repo , labelSelector )
@@ -231,6 +251,54 @@ func findArgocdAppByManifestPathAnnotation(ctx context.Context, componentPath st
231
251
return nil , fmt .Errorf ("No ArgoCD application found with manifest-generate-paths annotation that matches %s(looked at repo %s, checked %v apps) " , componentPath , repo , len (allRepoApps .Items ))
232
252
}
233
253
254
+ func SetArgoCDAppRevision (ctx context.Context , componentPath string , revision string , repo string , useSHALabelForArgoDicovery bool ) error {
255
+ var foundApp * argoappv1.Application
256
+ var err error
257
+ ac , err := createArgoCdClients ()
258
+ if err != nil {
259
+ return fmt .Errorf ("Error creating ArgoCD clients: %w" , err )
260
+ }
261
+ if useSHALabelForArgoDicovery {
262
+ foundApp , err = findArgocdAppBySHA1Label (ctx , componentPath , repo , ac .app )
263
+ } else {
264
+ foundApp , err = findArgocdAppByManifestPathAnnotation (ctx , componentPath , repo , ac .app )
265
+ }
266
+ if err != nil {
267
+ return fmt .Errorf ("error finding ArgoCD application for component path %s: %w" , componentPath , err )
268
+ }
269
+ if foundApp .Spec .Source .TargetRevision == revision {
270
+ log .Infof ("App %s already has revision %s" , foundApp .Name , revision )
271
+ return nil
272
+ }
273
+
274
+ patchObject := struct {
275
+ Spec struct {
276
+ Source struct {
277
+ TargetRevision string `json:"targetRevision"`
278
+ } `json:"source"`
279
+ } `json:"spec"`
280
+ }{}
281
+ patchObject .Spec .Source .TargetRevision = revision
282
+ patchJson , _ := json .Marshal (patchObject )
283
+ patch := string (patchJson )
284
+ log .Debugf ("Patching app %s/%s with: %s" , foundApp .Namespace , foundApp .Name , patch )
285
+
286
+ patchType := "merge"
287
+ _ , err = ac .app .Patch (ctx , & application.ApplicationPatchRequest {
288
+ Name : & foundApp .Name ,
289
+ AppNamespace : & foundApp .Namespace ,
290
+ PatchType : & patchType ,
291
+ Patch : & patch ,
292
+ })
293
+ if err != nil {
294
+ return fmt .Errorf ("revision patching failed: %w" , err )
295
+ } else {
296
+ log .Infof ("ArgoCD App %s revision set to %s" , foundApp .Name , revision )
297
+ }
298
+
299
+ return err
300
+ }
301
+
234
302
func generateDiffOfAComponent (ctx context.Context , componentPath string , prBranch string , repo string , appClient application.ApplicationServiceClient , projClient projectpkg.ProjectServiceClient , argoSettings * settings.Settings , useSHALabelForArgoDicovery bool ) (componentDiffResult DiffResult ) {
235
303
componentDiffResult .ComponentPath = componentPath
236
304
@@ -266,6 +334,12 @@ func generateDiffOfAComponent(ctx context.Context, componentPath string, prBranc
266
334
log .Debugf ("Got ArgoCD app %s" , app .Name )
267
335
componentDiffResult .ArgoCdAppName = app .Name
268
336
componentDiffResult .ArgoCdAppURL = fmt .Sprintf ("%s/applications/%s" , argoSettings .URL , app .Name )
337
+
338
+ if app .Spec .Source .TargetRevision == prBranch {
339
+ componentDiffResult .DiffError = fmt .Errorf ("App %s already has revision %s as Source Target Revision, skipping diff calculation" , app .Name , prBranch )
340
+ return componentDiffResult
341
+ }
342
+
269
343
resources , err := appClient .ManagedResources (ctx , & application.ResourcesQuery {ApplicationName : & app .Name , AppNamespace : & app .Namespace })
270
344
if err != nil {
271
345
componentDiffResult .DiffError = err
@@ -313,41 +387,21 @@ func GenerateDiffOfChangedComponents(ctx context.Context, componentPathList []st
313
387
hasComponentDiff = false
314
388
hasComponentDiffErrors = false
315
389
// env var should be centralized
316
- client , err := createArgoCdClient ()
390
+ ac , err := createArgoCdClients ()
317
391
if err != nil {
318
- log .Errorf ("Error creating ArgoCD client : %v" , err )
392
+ log .Errorf ("Error creating ArgoCD clients : %v" , err )
319
393
return false , true , nil , err
320
394
}
321
395
322
- conn , appClient , err := client .NewApplicationClient ()
323
- if err != nil {
324
- log .Errorf ("Error creating ArgoCD app client: %v" , err )
325
- return false , true , nil , err
326
- }
327
- defer argoio .Close (conn )
328
-
329
- conn , projClient , err := client .NewProjectClient ()
330
- if err != nil {
331
- log .Errorf ("Error creating ArgoCD project client: %v" , err )
332
- return false , true , nil , err
333
- }
334
- defer argoio .Close (conn )
335
-
336
- conn , settingClient , err := client .NewSettingsClient ()
337
- if err != nil {
338
- log .Errorf ("Error creating ArgoCD settings client: %v" , err )
339
- return false , true , nil , err
340
- }
341
- defer argoio .Close (conn )
342
- argoSettings , err := settingClient .Get (ctx , & settings.SettingsQuery {})
396
+ argoSettings , err := ac .setting .Get (ctx , & settings.SettingsQuery {})
343
397
if err != nil {
344
398
log .Errorf ("Error getting ArgoCD settings: %v" , err )
345
399
return false , true , nil , err
346
400
}
347
401
348
402
log .Debugf ("Checking ArgoCD diff for components: %v" , componentPathList )
349
403
for _ , componentPath := range componentPathList {
350
- currentDiffResult := generateDiffOfAComponent (ctx , componentPath , prBranch , repo , appClient , projClient , argoSettings , useSHALabelForArgoDicovery )
404
+ currentDiffResult := generateDiffOfAComponent (ctx , componentPath , prBranch , repo , ac . app , ac . project , argoSettings , useSHALabelForArgoDicovery )
351
405
if currentDiffResult .DiffError != nil {
352
406
log .Errorf ("Error generating diff for component %s: %v" , componentPath , currentDiffResult .DiffError )
353
407
hasComponentDiffErrors = true
0 commit comments