Skip to content

Commit 1089063

Browse files
committed
feat: haveACMHub and usage refactor
Return only true when the acm hub is managed by the pattern. This makes the edge case where there is an externally managed acm and managed clusters, cleaner. Prevents accidentally removing them during uninstall.
1 parent a3a23cb commit 1089063

3 files changed

Lines changed: 44 additions & 31 deletions

File tree

internal/controller/acm.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"fmt"
2323
"log"
24+
"strings"
2425

2526
kerrors "k8s.io/apimachinery/pkg/api/errors"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -30,12 +31,16 @@ import (
3031
func haveACMHub(r *PatternReconciler) bool {
3132
gvrMCH := schema.GroupVersionResource{Group: "operator.open-cluster-management.io", Version: "v1", Resource: "multiclusterhubs"}
3233

33-
_, err := r.dynamicClient.Resource(gvrMCH).Namespace("open-cluster-management").Get(context.Background(), "multiclusterhub", metav1.GetOptions{})
34+
mch, err := r.dynamicClient.Resource(gvrMCH).Namespace("open-cluster-management").Get(context.Background(), "multiclusterhub", metav1.GetOptions{})
3435
if err != nil {
3536
log.Printf("Error obtaining hub: %s\n", err)
3637
return false
3738
}
38-
return true
39+
40+
return strings.EqualFold(
41+
mch.GetAnnotations()["patterns.gitops.validatedpatterns.io/managed"],
42+
"true",
43+
)
3944
}
4045

4146
// listManagedClusters lists all ManagedCluster resources (excluding local-cluster)

internal/controller/acm_test.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var _ = Describe("HaveACMHub", func() {
4141

4242
})
4343

44-
Context("when the ACM Hub exists", func() {
44+
Context("when the ACM Hub exists, and owned by the pattern operator", func() {
4545
BeforeEach(func() {
4646
hub := &unstructured.Unstructured{
4747
Object: map[string]any{
@@ -50,6 +50,9 @@ var _ = Describe("HaveACMHub", func() {
5050
"metadata": map[string]any{
5151
"name": "multiclusterhub",
5252
"namespace": "open-cluster-management",
53+
"annotations": map[string]any{
54+
"patterns.gitops.validatedpatterns.io/managed": "true",
55+
},
5356
},
5457
},
5558
}
@@ -63,21 +66,30 @@ var _ = Describe("HaveACMHub", func() {
6366
})
6467
})
6568

66-
Context("when the ACM Hub does not exist", func() {
69+
Context("when the ACM Hub exists, and NOT owned by the pattern operator", func() {
70+
BeforeEach(func() {
71+
hub := &unstructured.Unstructured{
72+
Object: map[string]any{
73+
"apiVersion": "operator.open-cluster-management.io/v1",
74+
"kind": "MultiClusterHub",
75+
"metadata": map[string]any{
76+
"name": "multiclusterhub",
77+
"namespace": "open-cluster-management",
78+
},
79+
},
80+
}
81+
_, err := dynamicClient.Resource(gvrMCH).Namespace("open-cluster-management").Create(context.Background(), hub, metav1.CreateOptions{})
82+
Expect(err).ToNot(HaveOccurred())
83+
})
84+
6785
It("should return false", func() {
6886
result := haveACMHub(patternReconciler)
6987
Expect(result).To(BeFalse())
7088
})
7189
})
7290

73-
Context("when there is an error listing ConfigMaps", func() {
74-
BeforeEach(func() {
75-
kubeClient.PrependReactor("list", "configmaps", func(testing.Action) (handled bool, ret runtime.Object, err error) {
76-
return true, nil, fmt.Errorf("config map error")
77-
})
78-
})
79-
80-
It("should return false and log the error", func() {
91+
Context("when the ACM Hub does not exist", func() {
92+
It("should return false", func() {
8193
result := haveACMHub(patternReconciler)
8294
Expect(result).To(BeFalse())
8395
})

internal/controller/pattern_controller.go

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -721,17 +721,21 @@ func (r *PatternReconciler) deleteHubApps(targetApp, app *argoapi.Application, n
721721
// Delete managed clusters (excluding local-cluster)
722722
// These must be removed before hub deletion can proceed because ACM won't delete properly if they exist
723723
// we do not care about the error, since we might be on a standalone cluster
724-
managedClusters, _ := r.listManagedClusters(context.Background())
724+
// Only do this if the pattern is in charge of the acm hub
725725

726-
if len(managedClusters) > 0 {
727-
deletedCount, err := r.deleteManagedClusters(context.TODO())
728-
if err != nil {
729-
return fmt.Errorf("failed to delete managed clusters: %w", err)
730-
}
726+
if haveACMHub(r) {
727+
managedClusters, _ := r.listManagedClusters(context.Background())
728+
729+
if len(managedClusters) > 0 {
730+
deletedCount, err := r.deleteManagedClusters(context.TODO())
731+
if err != nil {
732+
return fmt.Errorf("failed to delete managed clusters: %w", err)
733+
}
731734

732-
if deletedCount > 0 {
733-
log.Printf("Deleted %d managed cluster(s), waiting for them to be fully removed", deletedCount)
734-
return fmt.Errorf("deleted %d managed cluster(s), waiting for removal to complete before proceeding with hub deletion", deletedCount)
735+
if deletedCount > 0 {
736+
log.Printf("Deleted %d managed cluster(s), waiting for them to be fully removed", deletedCount)
737+
return fmt.Errorf("deleted %d managed cluster(s), waiting for removal to complete before proceeding with hub deletion", deletedCount)
738+
}
735739
}
736740
}
737741

@@ -784,17 +788,9 @@ func (r *PatternReconciler) finalizeObject(instance *api.Pattern) error {
784788
// Initialize deletion phase if not set
785789
if qualifiedInstance.Status.DeletionPhase == api.InitializeDeletion {
786790
log.Printf("Initializing deletion phase")
787-
if haveACMHub(r) {
788-
if err := r.updateDeletionPhase(qualifiedInstance, api.DeleteSpokeChildApps); err != nil {
789-
return err
790-
}
791-
} else {
792-
// There is no acm/spoke, we can directly start cleaning up child apps (from hub)
793-
if err := r.updateDeletionPhase(qualifiedInstance, api.DeleteHubChildApps); err != nil {
794-
return err
795-
}
791+
if err := r.updateDeletionPhase(qualifiedInstance, api.DeleteSpokeChildApps); err != nil {
792+
return err
796793
}
797-
798794
return fmt.Errorf("initialized deletion phase, requeueing now")
799795
}
800796

0 commit comments

Comments
 (0)