@@ -20,6 +20,7 @@ import (
2020 "context"
2121
2222 argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1"
23+ argoprojv1a1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
2324 . "github.com/onsi/ginkgo/v2"
2425 . "github.com/onsi/gomega"
2526 "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture"
@@ -28,7 +29,9 @@ import (
2829 statefulsetFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/statefulset"
2930 fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils"
3031 appsv1 "k8s.io/api/apps/v1"
32+ corev1 "k8s.io/api/core/v1"
3133 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+ "k8s.io/apimachinery/pkg/types"
3235 "sigs.k8s.io/controller-runtime/pkg/client"
3336)
3437
@@ -49,8 +52,7 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() {
4952
5053 })
5154
52- It ("ensuring that extra arguments can be added to application controller" , func () {
53-
55+ It ("ensuring extra arguments are deduplicated, replaced, or preserved as expected in application-controller" , func () {
5456 By ("creating a simple ArgoCD CR and waiting for it to become available" )
5557 ns , cleanupFunc := fixture .CreateRandomE2ETestNamespaceWithCleanupFunc ()
5658 defer cleanupFunc ()
@@ -62,61 +64,250 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() {
6264 },
6365 }
6466 Expect (k8sClient .Create (ctx , argoCD )).To (Succeed ())
65-
6667 Eventually (argoCD , "5m" , "5s" ).Should (argocdFixture .BeAvailable ())
6768
68- By ("verifying app controller becomes availables" )
6969 appControllerSS := & appsv1.StatefulSet {
7070 ObjectMeta : metav1.ObjectMeta {
7171 Name : "example-argocd-application-controller" ,
7272 Namespace : ns .Name ,
7373 },
7474 }
75-
7675 Eventually (appControllerSS ).Should (k8sFixture .ExistByName ())
7776 Eventually (appControllerSS ).Should (statefulsetFixture .HaveReadyReplicas (1 ))
7877
79- By ("adding a new parameter via .spec.controller.extraCommandArgs" )
78+ // 1: Add new flag
79+ By ("adding a new flag via extraCommandArgs" )
8080 argocdFixture .Update (argoCD , func (ac * argov1beta1api.ArgoCD ) {
81- ac .Spec .Controller .ExtraCommandArgs = []string {"--app-hard-resync" }
81+ ac .Spec .Controller .ExtraCommandArgs = []string {"--app-hard-resync" , "2" }
8282 })
83-
84- By ("verifying new parameter is added, and the existing paramaters are still present" )
8583 Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("--app-hard-resync" , 0 ))
8684
87- Expect (len (appControllerSS .Spec .Template .Spec .Containers [0 ].Command )).To (BeNumerically (">=" , 10 ))
85+ // 2: Replace existing non-repeatable flag
86+ By ("replacing existing default flag with extraCommandArgs" )
87+ argocdFixture .Update (argoCD , func (ac * argov1beta1api.ArgoCD ) {
88+ ac .Spec .Controller .ExtraCommandArgs = []string {
89+ "--status-processors" , "15" ,
90+ "--kubectl-parallelism-limit" , "20" ,
91+ }
92+ })
93+
94+ // Expect new values to appear
95+ Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("--status-processors" , 0 ))
96+ Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("15" , 0 ))
97+ Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("--kubectl-parallelism-limit" , 0 ))
98+ Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("20" , 0 ))
8899
89- By ("removing the extra command arg" )
100+ // Expect default values to be replaced (old default 10 should not appear)
101+ Consistently (func () bool {
102+ cmd := appControllerSS .Spec .Template .Spec .Containers [0 ].Command
103+ for i := range cmd {
104+ if cmd [i ] == "--status-processors" && i + 1 < len (cmd ) && cmd [i + 1 ] == "10" {
105+ return true
106+ }
107+ }
108+ return false
109+ }).Should (BeFalse ())
110+
111+ // 3: Add duplicate flag+value pairs, which should be ignored
112+ By ("adding duplicate flags with same values" )
90113 argocdFixture .Update (argoCD , func (ac * argov1beta1api.ArgoCD ) {
91- ac .Spec .Controller .ExtraCommandArgs = nil
114+ ac .Spec .Controller .ExtraCommandArgs = []string {
115+ "--status-processors" , "15" , // duplicate
116+ "--kubectl-parallelism-limit" , "20" , // duplicate
117+ "--hydrator-enabled" ,
118+ }
92119 })
120+ // Verify --hydrator-enabled gets added
121+ Eventually (appControllerSS ).Should (statefulsetFixture .HaveContainerCommandSubstring ("--hydrator-enabled" , 0 ))
122+
123+ // But no duplicate --status-processors or --kubectl-parallelism-limit
124+ Consistently (func () bool {
125+ cmd := appControllerSS .Spec .Template .Spec .Containers [0 ].Command
93126
94- By ("verifying the parameter has been removed" )
95- Eventually (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--app-hard-resync" , 0 ))
96- Consistently (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--app-hard-resync" , 0 ))
97- Expect (len (appControllerSS .Spec .Template .Spec .Containers [0 ].Command )).To (BeNumerically (">=" , 10 ))
127+ statusProcessorsCount := 0
128+ kubectlLimitCount := 0
129+
130+ for i := 0 ; i < len (cmd ); i ++ {
131+ if cmd [i ] == "--status-processors" {
132+ statusProcessorsCount ++
133+ }
134+ if cmd [i ] == "--kubectl-parallelism-limit" {
135+ kubectlLimitCount ++
136+ }
137+ }
98138
99- By ("adding a new extra command arg that has the same name as existing parameters" )
139+ // Fail if either flag appears more than once
140+ return statusProcessorsCount > 1 || kubectlLimitCount > 1
141+ }).Should (BeFalse ())
142+
143+ // 4: Add a repeatable flag multiple times with different values
144+ By ("adding a repeatable flag with multiple values" )
100145 argocdFixture .Update (argoCD , func (ac * argov1beta1api.ArgoCD ) {
101146 ac .Spec .Controller .ExtraCommandArgs = []string {
102- "--status-processors" ,
103- "15" ,
104- "--kubectl-parallelism-limit" ,
105- "20" ,
147+ "--metrics-application-labels" , "application.argoproj.io/template-version" ,
148+ "--metrics-application-labels" , "application.argoproj.io/chart-version" ,
106149 }
107150 })
108151
109- // TODO: These lines are currently failing: they are ported correctly from the original kuttl test, but the original kuttl test did not check them correctly (and thus either the behaviour in the operator changed, or the tests never worked )
152+ Eventually ( appControllerSS ). Should ( statefulsetFixture . HaveContainerCommandSubstring ( "--metrics-application-labels" , 0 ) )
110153
111- // Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--status-processors 15", 0))
154+ // Check that both --metrics-application-labels flags are present
155+ Eventually (func () bool {
156+ cmd := appControllerSS .Spec .Template .Spec .Containers [0 ].Command
112157
113- // Consistently(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--status-processors 15", 0))
158+ metricVals := []string {}
159+ for i := 0 ; i < len (cmd ); i ++ {
160+ if cmd [i ] == "--metrics-application-labels" && i + 1 < len (cmd ) {
161+ metricVals = append (metricVals , cmd [i + 1 ])
162+ }
163+ }
114164
115- // Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit 20", 0))
165+ // Ensure both values are present
166+ hasMetricLabelTemplate := false
167+ hasMetricLabelChart := false
168+ for _ , v := range metricVals {
169+ if v == "application.argoproj.io/template-version" {
170+ hasMetricLabelTemplate = true
171+ }
172+ if v == "application.argoproj.io/chart-version" {
173+ hasMetricLabelChart = true
174+ }
175+ }
176+ return hasMetricLabelTemplate && hasMetricLabelChart
177+ }).Should (BeTrue ())
116178
117- // Consistently(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit 20", 0))
179+ // 5: Remove all extra args
180+ By ("removing all extra args" )
181+ argocdFixture .Update (argoCD , func (ac * argov1beta1api.ArgoCD ) {
182+ ac .Spec .Controller .ExtraCommandArgs = nil
183+ })
118184
185+ // Expect all custom flags to disappear
186+ Eventually (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--metrics-application-labels" , 0 ))
187+ Eventually (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--status-processors 15" , 0 ))
188+ Eventually (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--kubectl-parallelism-limit 20" , 0 ))
189+ Eventually (appControllerSS ).ShouldNot (statefulsetFixture .HaveContainerCommandSubstring ("--hydrator-enabled" , 0 ))
119190 })
191+ })
192+
193+ })
194+
195+ var _ = Describe ("ArgoCD Resource Health Persist" , func () {
196+ var (
197+ argoCDName = "example-argocd"
198+ appName = "guestbook"
199+ appNamespace = "guestbook"
200+ )
201+
202+ var (
203+ k8sClient client.Client
204+ ctx context.Context
205+ )
206+
207+ BeforeEach (func () {
208+ fixture .EnsureParallelCleanSlate ()
209+
210+ k8sClient , _ = fixtureUtils .GetE2ETestKubeClient ()
211+ ctx = context .Background ()
212+
213+ })
214+
215+ Context ("1-115_validate_resource_health_persisted_in_application_status" , func () {
216+ It ("should persist resource health in Application CR status when configured" , func () {
217+ ns , cleanupFunc := fixture .CreateRandomE2ETestNamespaceWithCleanupFunc ()
218+ defer cleanupFunc ()
219+
220+ err := k8sClient .Get (ctx , client.ObjectKey {
221+ Name : ns .Name ,
222+ Namespace : ns .Namespace ,
223+ }, ns )
224+ Expect (err ).Should (BeNil ())
120225
226+ By ("Creating ArgoCD CR with controller.resource.health.persist=true" )
227+ argoCD := & argov1beta1api.ArgoCD {
228+ ObjectMeta : metav1.ObjectMeta {Name : "example-argocd" , Namespace : ns .Name },
229+ Spec : argov1beta1api.ArgoCDSpec {
230+ Controller : argov1beta1api.ArgoCDApplicationControllerSpec {},
231+ },
232+ }
233+ Expect (k8sClient .Create (ctx , argoCD )).To (Succeed ())
234+ Eventually (argoCD , "5m" , "5s" ).Should (argocdFixture .BeAvailable ())
235+
236+ By ("Waiting for Application Controller to be ready" )
237+ Eventually (func () bool {
238+ deploy := & appsv1.StatefulSet {}
239+ err := k8sClient .Get (ctx , client.ObjectKey {
240+ Name : argoCDName + "-application-controller" ,
241+ Namespace : ns .Name ,
242+ }, deploy )
243+ return err == nil && deploy .Status .ReadyReplicas > 0
244+ }, "1m" , "5s" ).Should (BeTrue ())
245+
246+ targetNamespace := & corev1.Namespace {
247+ ObjectMeta : metav1.ObjectMeta {
248+ Name : appNamespace ,
249+ Labels : map [string ]string {
250+ "argocd.argoproj.io/managed-by" : ns .Name ,
251+ },
252+ },
253+ }
254+ By ("Creating target namespace for Application" )
255+ Expect (k8sClient .Create (ctx , targetNamespace )).To (Succeed ())
256+
257+ By ("Creating ArgoCD Application CR" )
258+ app := & argoprojv1a1.Application {
259+ ObjectMeta : metav1.ObjectMeta {
260+ Name : appName ,
261+ Namespace : ns .Name ,
262+ },
263+ Spec : argoprojv1a1.ApplicationSpec {
264+ Project : "default" ,
265+ Source : & argoprojv1a1.ApplicationSource {
266+ RepoURL : "https://github.com/Rizwana777/argocd-example-apps" ,
267+ Path : "guestbook" ,
268+ TargetRevision : "HEAD" ,
269+ },
270+ Destination : argoprojv1a1.ApplicationDestination {
271+ Server : "https://kubernetes.default.svc" ,
272+ Namespace : targetNamespace .Name ,
273+ },
274+ SyncPolicy : & argoprojv1a1.SyncPolicy {
275+ Automated : & argoprojv1a1.SyncPolicyAutomated {},
276+ },
277+ },
278+ }
279+ Expect (k8sClient .Create (ctx , app )).To (Succeed ())
280+
281+ By ("Validating that resource health is persisted in Application CR" )
282+ Eventually (func () bool {
283+ var fetched argoprojv1a1.Application
284+ err := k8sClient .Get (ctx , types.NamespacedName {
285+ Name : appName ,
286+ Namespace : ns .Name ,
287+ }, & fetched )
288+ if err != nil {
289+ return false
290+ }
291+
292+ // Check if application has resources with health information
293+ if len (fetched .Status .Resources ) == 0 {
294+ return false
295+ }
296+
297+ for _ , res := range fetched .Status .Resources {
298+ if res .Health == nil {
299+ return false
300+ }
301+ if res .Health .Status == "" {
302+ return false
303+ }
304+ }
305+
306+ // Validate resourceHealthSource is NOT present (it is omitted when health is persisted)
307+ return len (fetched .Status .ResourceHealthSource ) == 0
308+ }, "3m" , "5s" ).Should (BeTrue ())
309+
310+ Expect (k8sClient .Delete (ctx , targetNamespace )).To (Succeed ())
311+ })
121312 })
122313})
0 commit comments