Skip to content

Commit 222a229

Browse files
authored
feat: enhance addon lifecycle with dynamic templates and robust cleanup (#122)
- Replace static AddOnTemplate with per-GitOpsCluster dynamic generation - Implement pre-delete cleanup job for proper ArgoCD uninstall sequence - Add automatic placement tolerations for unreachable/unavailable clusters - Clean up addons when clusters are removed from placement - Use Kubernetes downward API for controller image configuration - Add comprehensive cleanup e2e test suite and CI workflow Signed-off-by: Mike Ng <[email protected]>
1 parent a9dc6f4 commit 222a229

32 files changed

+1825
-331
lines changed

.github/workflows/e2e.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,62 @@ jobs:
7777
env:
7878
KUBECONFIG: /home/runner/.kube/config
7979
run: make test-e2e
80+
81+
e2e-cleanup:
82+
name: e2e-cleanup
83+
runs-on: ubuntu-latest
84+
steps:
85+
- name: checkout code
86+
uses: actions/checkout@v4
87+
with:
88+
fetch-depth: 1
89+
path: go/src/open-cluster-management.io/argocd-pull-integration
90+
91+
- name: install Go
92+
uses: actions/setup-go@v5
93+
with:
94+
go-version: ${{ env.GO_VERSION }}
95+
96+
- name: Add GOPATH/bin to PATH
97+
run: echo "$GOPATH/bin" >> "$GITHUB_PATH"
98+
99+
- name: install imagebuilder
100+
run: go install github.com/openshift/imagebuilder/cmd/[email protected]
101+
102+
- name: Set up Helm
103+
uses: azure/setup-helm@v4
104+
with:
105+
version: v3.15.2
106+
107+
- name: Set up kubectl
108+
uses: azure/setup-kubectl@v4
109+
with:
110+
version: v1.29.0
111+
112+
- name: build-images
113+
run: make build-images
114+
115+
- name: setup kind (cluster1)
116+
uses: engineerd/[email protected]
117+
with:
118+
version: v0.14.0
119+
name: cluster1
120+
121+
- name: setup kind (hub)
122+
uses: engineerd/[email protected]
123+
with:
124+
version: v0.14.0
125+
name: hub
126+
127+
- name: Load image on the nodes of the hub
128+
run: |
129+
kind load docker-image --name=hub quay.io/open-cluster-management/argocd-pull-integration:latest
130+
131+
- name: Load image on the nodes of the cluster1
132+
run: |
133+
kind load docker-image --name=cluster1 quay.io/open-cluster-management/argocd-pull-integration:latest
134+
135+
- name: Run e2e cleanup test
136+
env:
137+
KUBECONFIG: /home/runner/.kube/config
138+
run: make test-e2e-cleanup

Makefile

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,57 @@ test-e2e-full: ## Complete e2e test with kind cluster setup, build, deployment,
163163
@echo " kubectl get application -n argocd --context kind-$(SPOKE_CLUSTER)"
164164
@echo " sleep 2"
165165

166+
.PHONY: test-e2e-cleanup
167+
test-e2e-cleanup: manifests generate fmt vet ## Run e2e cleanup tests (checks addon cleanup behavior)
168+
@echo "===== Installing MetalLB ====="
169+
./test/e2e/scripts/install_metallb.sh
170+
@echo ""
171+
@echo "===== Setting up OCM environment ====="
172+
./test/e2e/scripts/setup_ocm_env.sh
173+
@echo ""
174+
@echo "===== Installing addon via Helm ====="
175+
$(KUBECTL) config use-context kind-$(HUB_CLUSTER)
176+
$(KUBECTL) create namespace argocd || true
177+
$(KUBECTL) label namespace argocd app.kubernetes.io/managed-by=Helm --overwrite
178+
$(KUBECTL) annotate namespace argocd meta.helm.sh/release-name=argocd-agent-addon meta.helm.sh/release-namespace=argocd --overwrite
179+
helm install argocd-agent-addon \
180+
./charts/argocd-agent-addon \
181+
--namespace argocd \
182+
--set image=quay.io/open-cluster-management/argocd-pull-integration \
183+
--set tag=latest \
184+
--set addonImage=quay.io/open-cluster-management/argocd-pull-integration \
185+
--set addonTag=latest \
186+
--wait \
187+
--timeout 10m
188+
@echo ""
189+
@echo "===== Running cleanup e2e tests ====="
190+
go test -tags=e2e ./test/e2e/ -v -ginkgo.v --ginkgo.label-filter="cleanup"
191+
@echo ""
192+
@echo "===== E2E Cleanup Tests Complete ====="
193+
194+
.PHONY: test-e2e-cleanup-full
195+
test-e2e-cleanup-full: ## Complete e2e test with cleanup verification (cluster setup + deployment + cleanup)
196+
@echo "===== Cleaning up existing clusters ====="
197+
$(KIND) delete clusters --all || true
198+
@echo ""
199+
@echo "===== Creating KinD clusters ====="
200+
$(KIND) create cluster --name $(HUB_CLUSTER)
201+
$(KIND) create cluster --name $(SPOKE_CLUSTER)
202+
@echo ""
203+
@echo "===== Building controller image ====="
204+
$(MAKE) docker-build IMG=$(E2E_IMG)
205+
@echo ""
206+
@echo "===== Loading image to clusters ====="
207+
$(KIND) load docker-image $(E2E_IMG) --name $(HUB_CLUSTER)
208+
$(KIND) load docker-image $(E2E_IMG) --name $(SPOKE_CLUSTER)
209+
@echo ""
210+
@echo "===== Running cleanup e2e tests ====="
211+
$(MAKE) test-e2e-cleanup
212+
@echo ""
213+
@echo "===== E2E Cleanup Tests Complete ====="
214+
@echo "Hub context: kind-$(HUB_CLUSTER)"
215+
@echo "Spoke context: kind-$(SPOKE_CLUSTER)"
216+
166217
##@ Build
167218

168219
.PHONY: build

api/v1alpha1/gitopscluster_types.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ type ArgoCDAgentAddonSpec struct {
5454
// AgentImage is the ArgoCD agent image to use
5555
// +optional
5656
AgentImage string `json:"agentImage,omitempty"`
57-
58-
// Uninstall indicates whether to uninstall the addon
59-
// +optional
60-
Uninstall bool `json:"uninstall,omitempty"`
6157
}
6258

6359
// GitOpsClusterStatus defines the observed state of GitOpsCluster
@@ -98,6 +94,15 @@ const (
9894

9995
// ConditionAddonConfigured indicates that addon config and ManagedClusterAddon are configured
10096
ConditionAddonConfigured = "AddonConfigured"
97+
98+
// ConditionPlacementTolerationConfigured indicates that placement tolerations have been configured
99+
ConditionPlacementTolerationConfigured = "PlacementTolerationConfigured"
100+
101+
// ConditionAddOnTemplateReady indicates that AddOnTemplate has been created/updated
102+
ConditionAddOnTemplateReady = "AddOnTemplateReady"
103+
104+
// ConditionRemovedClustersCleanedUp indicates that removed clusters have been cleaned up
105+
ConditionRemovedClustersCleanedUp = "RemovedClustersCleanedUp"
101106
)
102107

103108
// Condition reasons

api/v1alpha1/gitopscluster_types_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ func TestArgoCDAgentAddonSpec(t *testing.T) {
8888
Mode: "managed",
8989
OperatorImage: "quay.io/operator:v1.0.0",
9090
AgentImage: "quay.io/agent:v1.0.0",
91-
Uninstall: false,
9291
},
9392
},
9493
{
@@ -103,9 +102,7 @@ func TestArgoCDAgentAddonSpec(t *testing.T) {
103102
},
104103
{
105104
name: "uninstall mode",
106-
spec: ArgoCDAgentAddonSpec{
107-
Uninstall: true,
108-
},
105+
spec: ArgoCDAgentAddonSpec{},
109106
},
110107
}
111108

charts/argocd-agent-addon/templates/addontemplate.yaml

Lines changed: 0 additions & 85 deletions
This file was deleted.

charts/argocd-agent-addon/templates/argocd-operator/argocd.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ metadata:
66
spec:
77
controller:
88
enabled: false
9+
applicationSet:
10+
enabled: true
911
argoCDAgent:
1012
principal:
1113
enabled: true

charts/argocd-agent-addon/templates/clustermanagementaddon.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,3 @@ spec:
1111
resource: addondeploymentconfigs
1212
- group: addon.open-cluster-management.io
1313
resource: addontemplates
14-
defaultConfig:
15-
name: argocd-agent-addon

charts/argocd-agent-addon/templates/controller/deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ spec:
3333
args:
3434
- --mode=controller
3535
- --leader-elect
36+
env:
37+
- name: CONTROLLER_IMAGE
38+
value: "{{ .Values.image }}:{{ .Values.tag }}"
3639
securityContext:
3740
readOnlyRootFilesystem: true
3841
allowPrivilegeEscalation: false

cmd/main.go

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ var (
7272
ArgoCDAgentServerAddress = ""
7373
ArgoCDAgentServerPort = ""
7474
ArgoCDAgentMode = "managed"
75-
ArgoCDAgentUninstall = false
7675
)
7776

7877
var (
@@ -104,7 +103,7 @@ func main() {
104103
var enableHTTP2 bool
105104
var tlsOpts []func(*tls.Config)
106105

107-
flag.StringVar(&mode, "mode", "controller", "Mode to run: controller or addon")
106+
flag.StringVar(&mode, "mode", "controller", "Mode to run: controller, addon, or cleanup")
108107
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
109108
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
110109
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -143,6 +142,12 @@ func main() {
143142
return
144143
}
145144

145+
// Check if running in cleanup mode
146+
if mode == "cleanup" {
147+
runCleanupMode()
148+
return
149+
}
150+
146151
// Continue with normal controller mode
147152

148153
// if the enable-http2 flag is false (the default), http/2 should be disabled
@@ -295,10 +300,6 @@ func runAddonMode() {
295300
ArgoCDAgentMode = newArgoCDAgentMode
296301
}
297302

298-
if newArgoCDAgentUninstall, found := os.LookupEnv("ARGOCD_AGENT_UNINSTALL"); found && newArgoCDAgentUninstall == "true" {
299-
ArgoCDAgentUninstall = true
300-
}
301-
302303
setupLog.Info("Addon mode settings",
303304
"syncInterval", addonOptions.SyncInterval,
304305
"leaseDuration", addonOptions.LeaderElectionLeaseDuration,
@@ -309,7 +310,6 @@ func runAddonMode() {
309310
"ArgoCDAgentServerAddress", ArgoCDAgentServerAddress,
310311
"ArgoCDAgentServerPort", ArgoCDAgentServerPort,
311312
"ArgoCDAgentMode", ArgoCDAgentMode,
312-
"ArgoCDAgentUninstall", ArgoCDAgentUninstall,
313313
)
314314

315315
// Create a new manager for addon mode
@@ -331,7 +331,7 @@ func runAddonMode() {
331331

332332
// Setup the addon with the manager
333333
if err = addon.SetupWithManager(mgr, addonOptions.SyncInterval,
334-
ArgoCDOperatorImage, ArgoCDAgentImage, ArgoCDAgentServerAddress, ArgoCDAgentServerPort, ArgoCDAgentMode, ArgoCDAgentUninstall); err != nil {
334+
ArgoCDOperatorImage, ArgoCDAgentImage, ArgoCDAgentServerAddress, ArgoCDAgentServerPort, ArgoCDAgentMode); err != nil {
335335
setupLog.Error(err, "unable to create addon controller")
336336
os.Exit(1)
337337
}
@@ -342,3 +342,47 @@ func runAddonMode() {
342342
os.Exit(1)
343343
}
344344
}
345+
346+
// runCleanupMode runs the application in cleanup mode for pre-delete hook
347+
func runCleanupMode() {
348+
setupLog.Info("Starting in cleanup mode")
349+
350+
// Read environment variables for cleanup configuration
351+
if newArgoCDOperatorImage, found := os.LookupEnv("ARGOCD_OPERATOR_IMAGE"); found && newArgoCDOperatorImage != "" {
352+
ArgoCDOperatorImage = newArgoCDOperatorImage
353+
}
354+
355+
if newArgoCDAgentImage, found := os.LookupEnv("ARGOCD_AGENT_IMAGE"); found && newArgoCDAgentImage != "" {
356+
ArgoCDAgentImage = newArgoCDAgentImage
357+
}
358+
359+
setupLog.Info("Cleanup mode settings",
360+
"ArgoCDOperatorImage", ArgoCDOperatorImage,
361+
"ArgoCDAgentImage", ArgoCDAgentImage,
362+
)
363+
364+
// Create a manager for cleanup mode (no leader election for cleanup job)
365+
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
366+
Scheme: scheme,
367+
Metrics: metricsserver.Options{
368+
BindAddress: "0", // Disable metrics in cleanup mode
369+
},
370+
LeaderElection: false, // Cleanup job doesn't need leader election
371+
})
372+
if err != nil {
373+
setupLog.Error(err, "unable to start cleanup manager")
374+
os.Exit(1)
375+
}
376+
377+
// Setup the cleanup with the manager
378+
if err = addon.SetupCleanupWithManager(mgr, ArgoCDOperatorImage, ArgoCDAgentImage); err != nil {
379+
setupLog.Error(err, "unable to create cleanup controller")
380+
os.Exit(1)
381+
}
382+
383+
setupLog.Info("starting cleanup manager")
384+
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
385+
setupLog.Error(err, "problem running cleanup manager")
386+
os.Exit(1)
387+
}
388+
}

config/crd/bases/apps.open-cluster-management.io_gitopsclusters.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ spec:
6868
description: PrincipalServerPort is the port of the ArgoCD principal
6969
server
7070
type: string
71-
uninstall:
72-
description: Uninstall indicates whether to uninstall the addon
73-
type: boolean
7471
type: object
7572
placementRef:
7673
description: PlacementRef references a Placement resource to select

0 commit comments

Comments
 (0)