Skip to content

Commit e424e31

Browse files
committed
Add e2e ginkgo tests for extraCommandargs and Application Health Status
Signed-off-by: Rizwana777 <[email protected]>
1 parent 9cc7224 commit e424e31

File tree

2 files changed

+266
-27
lines changed

2 files changed

+266
-27
lines changed

test/openshift/e2e/ginkgo/parallel/1-115_validate_controller_extra_command_args_test.go

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() {
4949

5050
})
5151

52-
It("ensuring that extra arguments can be added to application controller", func() {
53-
52+
It("ensures extra arguments are deduplicated, replaced, or preserved as expected in application-controller", func() {
5453
By("creating a simple ArgoCD CR and waiting for it to become available")
5554
ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc()
5655
defer cleanupFunc()
@@ -62,61 +61,156 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() {
6261
},
6362
}
6463
Expect(k8sClient.Create(ctx, argoCD)).To(Succeed())
65-
6664
Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable())
6765

68-
By("verifying app controller becomes availables")
6966
appControllerSS := &appsv1.StatefulSet{
7067
ObjectMeta: metav1.ObjectMeta{
7168
Name: "example-argocd-application-controller",
7269
Namespace: ns.Name,
7370
},
7471
}
75-
7672
Eventually(appControllerSS).Should(k8sFixture.ExistByName())
7773
Eventually(appControllerSS).Should(statefulsetFixture.HaveReadyReplicas(1))
7874

79-
By("adding a new parameter via .spec.controller.extraCommandArgs")
75+
// Verify default values of --status-processors and --kubectl-parallelism-limit
76+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--status-processors", 0))
77+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("20", 0))
78+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit", 0))
79+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("10", 0))
80+
81+
// 1: Add new flag
82+
By("adding a new flag via extraCommandArgs")
8083
argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) {
81-
ac.Spec.Controller.ExtraCommandArgs = []string{"--app-hard-resync"}
84+
ac.Spec.Controller.ExtraCommandArgs = []string{"--app-hard-resync", "2"}
8285
})
83-
84-
By("verifying new parameter is added, and the existing paramaters are still present")
8586
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--app-hard-resync", 0))
8687

87-
Expect(len(appControllerSS.Spec.Template.Spec.Containers[0].Command)).To(BeNumerically(">=", 10))
88-
89-
By("removing the extra command arg")
88+
// 2: Replace existing non-repeatable flags --status-processors and --kubectl-parallelism-limit
89+
By("replacing existing default flag with extraCommandArgs")
9090
argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) {
91-
ac.Spec.Controller.ExtraCommandArgs = nil
91+
ac.Spec.Controller.ExtraCommandArgs = []string{
92+
"--status-processors", "15",
93+
"--kubectl-parallelism-limit", "20",
94+
}
9295
})
9396

94-
By("verifying the parameter has been removed")
97+
By("new values should appear for --status-processors and --kubectl-parallelism-limit")
98+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--status-processors", 0))
99+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("15", 0))
100+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit", 0))
101+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("20", 0))
95102
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))
98103

99-
By("adding a new extra command arg that has the same name as existing parameters")
104+
Expect(k8sClient.Get(ctx, client.ObjectKey{
105+
Name: appControllerSS.Name,
106+
Namespace: appControllerSS.Namespace,
107+
}, appControllerSS)).To(Succeed())
108+
109+
By("default values should be replaced (old default for --status-processors 20 and --kubectl-parallelism-limit 10 should not appear")
110+
Consistently(func() bool {
111+
cmd := appControllerSS.Spec.Template.Spec.Containers[0].Command
112+
for i := range cmd {
113+
if cmd[i] == "--status-processors" && i+1 < len(cmd) && cmd[i+1] == "20" {
114+
return true
115+
}
116+
if cmd[i] == "--kubectl-parallelism-limit" && i+1 < len(cmd) && cmd[i+1] == "10" {
117+
return true
118+
}
119+
}
120+
return false
121+
}).Should(BeFalse())
122+
123+
// 3: Add duplicate flag+value pairs, which should be ignored
124+
By("adding duplicate flags with same values")
100125
argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) {
101126
ac.Spec.Controller.ExtraCommandArgs = []string{
102-
"--status-processors",
103-
"15",
104-
"--kubectl-parallelism-limit",
105-
"20",
127+
"--status-processors", "15",
128+
"--kubectl-parallelism-limit", "20",
129+
"--status-processors", "15", // duplicate
130+
"--kubectl-parallelism-limit", "20", // duplicate
131+
"--hydrator-enabled",
106132
}
107133
})
134+
// Verify --hydrator-enabled gets added
135+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--hydrator-enabled", 0))
136+
137+
Expect(k8sClient.Get(ctx, client.ObjectKey{
138+
Name: appControllerSS.Name,
139+
Namespace: appControllerSS.Namespace,
140+
}, appControllerSS)).To(Succeed())
141+
142+
// But no duplicate --status-processors or --kubectl-parallelism-limit
143+
Consistently(func() bool {
144+
cmd := appControllerSS.Spec.Template.Spec.Containers[0].Command
145+
146+
statusProcessorsCount := 0
147+
kubectlLimitCount := 0
148+
149+
for i := 0; i < len(cmd); i++ {
150+
if cmd[i] == "--status-processors" {
151+
statusProcessorsCount++
152+
}
153+
if cmd[i] == "--kubectl-parallelism-limit" {
154+
kubectlLimitCount++
155+
}
156+
}
108157

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)
158+
// Fail if either flag appears more than once
159+
return statusProcessorsCount > 1 || kubectlLimitCount > 1
160+
}).Should(BeFalse())
110161

111-
// Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--status-processors 15", 0))
162+
// 4: Add a repeatable flag multiple times with different values
163+
By("adding a repeatable flag with multiple values")
164+
argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) {
165+
ac.Spec.Controller.ExtraCommandArgs = []string{
166+
"--metrics-application-labels", "application.argoproj.io/template-version",
167+
"--metrics-application-labels", "application.argoproj.io/chart-version",
168+
}
169+
})
112170

113-
// Consistently(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--status-processors 15", 0))
171+
Eventually(appControllerSS).Should(statefulsetFixture.HaveContainerCommandSubstring("--metrics-application-labels", 0))
114172

115-
// Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit 20", 0))
173+
Expect(k8sClient.Get(ctx, client.ObjectKey{
174+
Name: appControllerSS.Name,
175+
Namespace: appControllerSS.Namespace,
176+
}, appControllerSS)).To(Succeed())
116177

117-
// Consistently(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit 20", 0))
178+
By("Check that both --metrics-application-labels flags are present")
179+
Eventually(func() bool {
180+
cmd := appControllerSS.Spec.Template.Spec.Containers[0].Command
118181

119-
})
182+
metricVals := []string{}
183+
for i := 0; i < len(cmd); i++ {
184+
if cmd[i] == "--metrics-application-labels" && i+1 < len(cmd) {
185+
metricVals = append(metricVals, cmd[i+1])
186+
}
187+
}
188+
189+
// Ensure both values are present
190+
hasMetricLabelTemplate := false
191+
hasMetricLabelChart := false
192+
for _, v := range metricVals {
193+
if v == "application.argoproj.io/template-version" {
194+
hasMetricLabelTemplate = true
195+
}
196+
if v == "application.argoproj.io/chart-version" {
197+
hasMetricLabelChart = true
198+
}
199+
}
200+
return hasMetricLabelTemplate && hasMetricLabelChart
201+
}).Should(BeTrue())
202+
203+
// 5: Remove all extra args
204+
By("removing all extra args")
205+
argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) {
206+
ac.Spec.Controller.ExtraCommandArgs = nil
207+
})
120208

209+
// Expect all custom flags to disappear
210+
Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--metrics-application-labels", 0))
211+
Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--status-processors 15", 0))
212+
Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--kubectl-parallelism-limit 20", 0))
213+
Eventually(appControllerSS).ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--hydrator-enabled", 0))
214+
})
121215
})
122216
})
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
Copyright 2025.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package parallel
18+
19+
import (
20+
"context"
21+
22+
argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1"
23+
argoprojv1a1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
24+
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
"github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture"
27+
argocdFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/argocd"
28+
ssFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/statefulset"
29+
fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils"
30+
appsv1 "k8s.io/api/apps/v1"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/types"
33+
34+
"sigs.k8s.io/controller-runtime/pkg/client"
35+
)
36+
37+
var _ = Describe("GitOps Operator Parallel E2E Test", func() {
38+
const (
39+
argoCDName = "example-argocd"
40+
appName = "guestbook"
41+
appNamespace = "guestbook-1-120"
42+
)
43+
44+
var (
45+
k8sClient client.Client
46+
ctx context.Context
47+
)
48+
49+
BeforeEach(func() {
50+
fixture.EnsureParallelCleanSlate()
51+
52+
k8sClient, _ = fixtureUtils.GetE2ETestKubeClient()
53+
ctx = context.Background()
54+
55+
})
56+
57+
Context("1-120_validate_resource_health_persisted_in_application_status", func() {
58+
It("should persist resource health in Application CR status when configured", func() {
59+
ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc()
60+
defer cleanupFunc()
61+
62+
Expect(k8sClient.Get(ctx, client.ObjectKey{
63+
Name: ns.Name,
64+
Namespace: ns.Namespace,
65+
}, ns)).To(Succeed())
66+
67+
By("Creating ArgoCD CR with controller.resource.health.persist=true, which is the default")
68+
argoCD := &argov1beta1api.ArgoCD{
69+
ObjectMeta: metav1.ObjectMeta{Name: "example-argocd", Namespace: ns.Name},
70+
Spec: argov1beta1api.ArgoCDSpec{
71+
Controller: argov1beta1api.ArgoCDApplicationControllerSpec{},
72+
},
73+
}
74+
Expect(k8sClient.Create(ctx, argoCD)).To(Succeed())
75+
Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable())
76+
77+
By("Waiting for Application Controller to be ready")
78+
ss := &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{
79+
Name: argoCDName + "-application-controller",
80+
Namespace: ns.Name,
81+
}}
82+
Eventually(ss).Should(ssFixture.HaveReadyReplicas(1))
83+
84+
targetNamespace, cleanupFunc := fixture.CreateManagedNamespaceWithCleanupFunc(appNamespace, ns.Name)
85+
defer cleanupFunc()
86+
87+
By("Creating ArgoCD Application CR")
88+
app := &argoprojv1a1.Application{
89+
ObjectMeta: metav1.ObjectMeta{
90+
Name: appName,
91+
Namespace: ns.Name,
92+
},
93+
Spec: argoprojv1a1.ApplicationSpec{
94+
Project: "default",
95+
Source: &argoprojv1a1.ApplicationSource{
96+
RepoURL: "https://github.com/redhat-developer/gitops-operator",
97+
Path: "guestbook",
98+
TargetRevision: "HEAD",
99+
},
100+
Destination: argoprojv1a1.ApplicationDestination{
101+
Server: "https://kubernetes.default.svc",
102+
Namespace: targetNamespace.Name,
103+
},
104+
SyncPolicy: &argoprojv1a1.SyncPolicy{
105+
Automated: &argoprojv1a1.SyncPolicyAutomated{},
106+
},
107+
},
108+
}
109+
Expect(k8sClient.Create(ctx, app)).To(Succeed())
110+
111+
By("Validating that resource health is persisted in Application CR")
112+
Eventually(func() bool {
113+
var fetched argoprojv1a1.Application
114+
err := k8sClient.Get(ctx, types.NamespacedName{
115+
Name: appName,
116+
Namespace: ns.Name,
117+
}, &fetched)
118+
if err != nil {
119+
GinkgoWriter.Println("failed to fetch Application:", err)
120+
return false
121+
}
122+
123+
// Check if application has resources with health information
124+
if len(fetched.Status.Resources) == 0 {
125+
GinkgoWriter.Println("Application.Status.Resources is empty")
126+
return false
127+
}
128+
129+
for _, res := range fetched.Status.Resources {
130+
if res.Health == nil {
131+
GinkgoWriter.Println("Resource", res.Kind, res.Name, "has nil Health")
132+
return false
133+
}
134+
if res.Health.Status == "" {
135+
GinkgoWriter.Println("Resource", res.Kind, res.Name, "has empty Health.Status")
136+
return false
137+
}
138+
}
139+
140+
// Validate resourceHealthSource is NOT present (it is omitted when health is persisted)
141+
return len(fetched.Status.ResourceHealthSource) == 0
142+
}, "3m", "5s").Should(BeTrue())
143+
})
144+
})
145+
})

0 commit comments

Comments
 (0)