11package application
22
33import (
4+ "context"
45 "encoding/json"
56 "fmt"
67 "os/exec"
@@ -9,10 +10,20 @@ import (
910 . "github.com/onsi/ginkgo/v2"
1011 . "github.com/onsi/gomega"
1112 matcher "github.com/onsi/gomega/types"
13+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+ "k8s.io/apimachinery/pkg/runtime/schema"
15+ "k8s.io/apimachinery/pkg/types"
1216
1317 argocdFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/argocd"
18+ fixtureUtils "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/utils"
1419)
1520
21+ var applicationGVR = schema.GroupVersionResource {
22+ Group : "argoproj.io" ,
23+ Version : "v1alpha1" ,
24+ Resource : "applications" ,
25+ }
26+
1627// AppRef is a lightweight reference to an Argo CD Application.
1728type AppRef struct {
1829 Name string
@@ -135,7 +146,7 @@ func WithSession(s *argocdFixture.Session) AppOption {
135146 return func (c * appConfig ) { c .session = s }
136147}
137148
138- // Create creates an Argo CD Application using the argocd CLI in login mode .
149+ // Create creates an Argo CD Application using the argocd CLI.
139150func Create (name , namespace string , opts ... AppOption ) * AppRef {
140151 cfg := & appConfig {}
141152 for _ , o := range opts {
@@ -150,38 +161,58 @@ func Create(name, namespace string, opts ...AppOption) *AppRef {
150161
151162 ref := & AppRef {Name : name , Namespace : namespace , session : cfg .session }
152163
153- // Post-create: annotations via kubectl
154- for k , v := range cfg .annotations {
155- out , err := runKubectl ("annotate" , "application.argoproj.io" , name , "-n" , namespace ,
156- fmt .Sprintf ("%s=%s" , k , v ))
157- Expect (err ).ToNot (HaveOccurred (), "kubectl annotate failed: %s" , out )
164+ // Post-create: apply annotations, labels, and managed namespace metadata via k8s client
165+ if len (cfg .annotations ) > 0 || len (cfg .labels ) > 0 || len (cfg .managedNSLabels ) > 0 {
166+ patchApp (name , namespace , cfg .annotations , cfg .labels , cfg .managedNSLabels )
158167 }
159168
160- // Post-create: labels via kubectl
161- for k , v := range cfg .labels {
162- out , err := runKubectl ("label" , "application.argoproj.io" , name , "-n" , namespace ,
163- fmt .Sprintf ("%s=%s" , k , v ))
164- Expect (err ).ToNot (HaveOccurred (), "kubectl label failed: %s" , out )
169+ return ref
170+ }
171+
172+ // patchApp applies annotations, labels, and managed namespace metadata to an Application using the k8s client.
173+ func patchApp (name , namespace string , annotations , labels map [string ]string , managedNSLabels map [string ]string ) {
174+ k8sClient , _ := fixtureUtils .GetE2ETestKubeClient ()
175+ ctx := context .Background ()
176+
177+ app := & unstructured.Unstructured {}
178+ app .SetGroupVersionKind (applicationGVR .GroupVersion ().WithKind ("Application" ))
179+ Expect (k8sClient .Get (ctx , types.NamespacedName {Name : name , Namespace : namespace }, app )).To (Succeed (), "failed to get Application %s/%s" , namespace , name )
180+
181+ if len (annotations ) > 0 {
182+ existing := app .GetAnnotations ()
183+ if existing == nil {
184+ existing = make (map [string ]string )
185+ }
186+ for k , v := range annotations {
187+ existing [k ] = v
188+ }
189+ app .SetAnnotations (existing )
165190 }
166191
167- // Post-create: managed namespace metadata labels via kubectl patch
168- if len (cfg .managedNSLabels ) > 0 {
169- patch := map [string ]any {
170- "spec" : map [string ]any {
171- "syncPolicy" : map [string ]any {
172- "managedNamespaceMetadata" : map [string ]any {
173- "labels" : cfg .managedNSLabels ,
174- },
175- },
176- },
192+ if len (labels ) > 0 {
193+ existing := app .GetLabels ()
194+ if existing == nil {
195+ existing = make (map [string ]string )
177196 }
178- patchBytes , _ := json . Marshal ( patch )
179- out , err := runKubectl ( "patch" , "application.argoproj.io" , name , "-n" , namespace ,
180- "--type=merge" , "-p" , string ( patchBytes ))
181- Expect ( err ). ToNot ( HaveOccurred (), "kubectl patch failed: %s" , out )
197+ for k , v := range labels {
198+ existing [ k ] = v
199+ }
200+ app . SetLabels ( existing )
182201 }
183202
184- return ref
203+ if len (managedNSLabels ) > 0 {
204+ spec , _ , _ := unstructured .NestedMap (app .Object , "spec" )
205+ if spec == nil {
206+ spec = map [string ]interface {}{}
207+ }
208+ labelsMap := make (map [string ]interface {}, len (managedNSLabels ))
209+ for k , v := range managedNSLabels {
210+ labelsMap [k ] = v
211+ }
212+ Expect (unstructured .SetNestedField (app .Object , labelsMap , "spec" , "syncPolicy" , "managedNamespaceMetadata" , "labels" )).To (Succeed ())
213+ }
214+
215+ Expect (k8sClient .Update (ctx , app )).To (Succeed (), "failed to update Application %s/%s" , namespace , name )
185216}
186217
187218// Delete deletes an Argo CD Application.
@@ -192,7 +223,7 @@ func Delete(ref *AppRef) {
192223}
193224
194225// Ref creates a reference to an existing Application without creating it.
195- // Session is optional — when nil, kubectl is used for get operations (matchers) .
226+ // Session is optional — when nil, argocd CLI get falls back to the k8s client .
196227func Ref (name , namespace string , sessions ... * argocdFixture.Session ) * AppRef {
197228 var session * argocdFixture.Session
198229 if len (sessions ) > 0 {
@@ -311,11 +342,14 @@ func getAppJSON(ref *AppRef) (map[string]any, error) {
311342 return nil , fmt .Errorf ("argocd app get failed: %v, output: %s" , err , output )
312343 }
313344 } else {
314- // No session — use kubectl directly (for Ref-only usage without CLI login)
315- output , err = runKubectl ("get" , "application.argoproj.io" , ref .Name , "-n" , ref .Namespace , "-o" , "json" )
316- if err != nil {
317- return nil , fmt .Errorf ("kubectl get application failed: %v, output: %s" , err , output )
345+ // No session — use k8s client directly (for Ref-only usage without CLI login)
346+ k8sClient , _ := fixtureUtils .GetE2ETestKubeClient ()
347+ app := & unstructured.Unstructured {}
348+ app .SetGroupVersionKind (applicationGVR .GroupVersion ().WithKind ("Application" ))
349+ if err := k8sClient .Get (context .Background (), types.NamespacedName {Name : ref .Name , Namespace : ref .Namespace }, app ); err != nil {
350+ return nil , fmt .Errorf ("k8s client get application failed: %v" , err )
318351 }
352+ return app .Object , nil
319353 }
320354
321355 var result map [string ]any
@@ -353,12 +387,3 @@ func runArgoCDCLI(session *argocdFixture.Session, args ...string) (string, error
353387 GinkgoWriter .Println (string (output ))
354388 return string (output ), err
355389}
356-
357- func runKubectl (args ... string ) (string , error ) {
358- GinkgoWriter .Println ("executing kubectl" , args )
359- // #nosec G204 -- test code
360- cmd := exec .Command ("kubectl" , args ... )
361- output , err := cmd .CombinedOutput ()
362- GinkgoWriter .Println (string (output ))
363- return string (output ), err
364- }
0 commit comments