diff --git a/operator/api/operator/v1alpha4/multiclusterglobalhub_types.go b/operator/api/operator/v1alpha4/multiclusterglobalhub_types.go index 0e681841e..8dafc9eeb 100644 --- a/operator/api/operator/v1alpha4/multiclusterglobalhub_types.go +++ b/operator/api/operator/v1alpha4/multiclusterglobalhub_types.go @@ -107,6 +107,13 @@ type MulticlusterGlobalHubSpec struct { // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} // +optional EnableMetrics bool `json:"enableMetrics"` + // InstallAgentOnLocal determines whether deploy the Global Hub Agent on the local hub cluster or not. + // If set to true, the Global Hub Agent will be installed on the local hub cluster only. + // If set to false, the Global Hub Agent will not be installed on the local hub cluster. + // Currently, switching the value of this field is not supported after the Global Hub is installed. + // +kubebuilder:default=false + // +optional + InstallAgentOnLocal bool `json:"InstallAgentOnLocal,omitempty"` } type AdvancedSpec struct { diff --git a/operator/bundle/manifests/operator.open-cluster-management.io_multiclusterglobalhubs.yaml b/operator/bundle/manifests/operator.open-cluster-management.io_multiclusterglobalhubs.yaml index 496353f3c..a2826c42d 100644 --- a/operator/bundle/manifests/operator.open-cluster-management.io_multiclusterglobalhubs.yaml +++ b/operator/bundle/manifests/operator.open-cluster-management.io_multiclusterglobalhubs.yaml @@ -55,6 +55,14 @@ spec: retention: 18m description: Spec specifies the desired state of multicluster global hub properties: + InstallAgentOnLocal: + default: false + description: |- + InstallAgentOnLocal determines whether deploy the Global Hub Agent on the local hub cluster or not. + If set to true, the Global Hub Agent will be installed on the local hub cluster only. + If set to false, the Global Hub Agent will not be installed on the local hub cluster. + Currently, switching the value of this field is not supported after the Global Hub is installed. + type: boolean advanced: description: AdvancedSpec specifies the advanced configurations for the multicluster global hub diff --git a/operator/config/crd/bases/operator.open-cluster-management.io_multiclusterglobalhubs.yaml b/operator/config/crd/bases/operator.open-cluster-management.io_multiclusterglobalhubs.yaml index d632e8f82..1adedd7a7 100644 --- a/operator/config/crd/bases/operator.open-cluster-management.io_multiclusterglobalhubs.yaml +++ b/operator/config/crd/bases/operator.open-cluster-management.io_multiclusterglobalhubs.yaml @@ -55,6 +55,14 @@ spec: retention: 18m description: Spec specifies the desired state of multicluster global hub properties: + InstallAgentOnLocal: + default: false + description: |- + InstallAgentOnLocal determines whether deploy the Global Hub Agent on the local hub cluster or not. + If set to true, the Global Hub Agent will be installed on the local hub cluster only. + If set to false, the Global Hub Agent will not be installed on the local hub cluster. + Currently, switching the value of this field is not supported after the Global Hub is installed. + type: boolean advanced: description: AdvancedSpec specifies the advanced configurations for the multicluster global hub diff --git a/operator/pkg/controllers/agent/default_agent_controller.go b/operator/pkg/controllers/agent/default_agent_controller.go index b30346133..f5b4677e1 100644 --- a/operator/pkg/controllers/agent/default_agent_controller.go +++ b/operator/pkg/controllers/agent/default_agent_controller.go @@ -261,7 +261,24 @@ func (r *DefaultAgentController) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } - return ctrl.Result{}, r.reconcileAddonAndResources(ctx, cluster, clusterManagementAddOn) + if mgh.Spec.InstallAgentOnLocal { + // if installed agent on hub cluster, global hub is installed in a brownfield cluster + if cluster.GetName() == constants.LocalClusterName || + cluster.Labels[constants.LocalClusterName] == "true" || + deployMode == operatorconstants.GHAgentDeployModeDefault || + deployMode == operatorconstants.GHAgentDeployModeHosted { + return ctrl.Result{}, r.reconcileAddonAndResources(ctx, cluster, clusterManagementAddOn) + } + } else { + // if not installed agent on hub cluster, global hub is installed in a greenfield cluster + if cluster.GetName() == constants.LocalClusterName || + cluster.Labels[constants.LocalClusterName] == "true" { + return ctrl.Result{}, nil + } + return ctrl.Result{}, r.reconcileAddonAndResources(ctx, cluster, clusterManagementAddOn) + } + + return ctrl.Result{}, nil } func (r *DefaultAgentController) deleteClusterManagementAddon(ctx context.Context) error { @@ -457,6 +474,5 @@ func GetAllManagedHubNames(ctx context.Context, c client.Client) ([]string, erro func filterManagedCluster(obj client.Object) bool { return obj.GetLabels()["vendor"] != "OpenShift" || - obj.GetLabels()["openshiftVersion"] == "3" || - obj.GetName() == constants.LocalClusterName + obj.GetLabels()["openshiftVersion"] == "3" } diff --git a/test/integration/operator/controllers/agent/cluster_default_addon_test.go b/test/integration/operator/controllers/agent/cluster_default_addon_test.go index 0e87b689b..c47e406a2 100644 --- a/test/integration/operator/controllers/agent/cluster_default_addon_test.go +++ b/test/integration/operator/controllers/agent/cluster_default_addon_test.go @@ -11,11 +11,12 @@ import ( clusterv1 "open-cluster-management.io/api/cluster/v1" workv1 "open-cluster-management.io/api/work/v1" + globalhubv1alpha4 "github.com/stolostron/multicluster-global-hub/operator/api/operator/v1alpha4" operatorconstants "github.com/stolostron/multicluster-global-hub/operator/pkg/constants" "github.com/stolostron/multicluster-global-hub/pkg/constants" ) -// go test ./test/integration/operator/agent -ginkgo.focus "deploy default addon" -v +// go test ./test/integration/operator/controllers/agent -ginkgo.focus "deploy default addon" -v var _ = Describe("deploy default addon", func() { It("Should create agent when importing an bare OCP", func() { clusterName := fmt.Sprintf("hub-%s", rand.String(6)) @@ -132,4 +133,57 @@ var _ = Describe("deploy default addon", func() { // contains both the ACM and the Global Hub manifests Expect(len(work.Spec.Workload.Manifests)).Should(Equal(17)) }) + + It("Should create agent for the local-cluster", func() { + By("set InstallAgentOnLocal to true") + mghLookupKey := types.NamespacedName{Namespace: "default", Name: MGHName} + existingMGH := &globalhubv1alpha4.MulticlusterGlobalHub{} + Eventually(func() bool { + err := runtimeClient.Get(ctx, mghLookupKey, existingMGH) + return err == nil + }, timeout, interval).Should(BeTrue()) + existingMGH.Spec.InstallAgentOnLocal = true + Expect(runtimeClient.Update(ctx, existingMGH)).Should(Succeed()) + + clusterName := fmt.Sprintf("hub-%s", rand.String(6)) + workName := fmt.Sprintf("addon-%s-deploy-0", + constants.GHManagedClusterAddonName) + + By("By preparing an OCP Managed Clusters") + prepareCluster(clusterName, + map[string]string{"vendor": "OpenShift", "local-cluster": "true"}, + map[string]string{}, + []clusterv1.ManagedClusterClaim{}, + clusterAvailableCondition) + + By("By checking the addon CR is is created in the cluster ns") + addon := &addonv1alpha1.ManagedClusterAddOn{} + Eventually(func() error { + return runtimeClient.Get(ctx, types.NamespacedName{ + Name: constants.GHManagedClusterAddonName, + Namespace: clusterName, + }, addon) + }, timeout, interval).ShouldNot(HaveOccurred()) + + Expect(len(addon.GetAnnotations())).Should(Equal(0)) + + By("By checking the agent manifestworks are created for the local cluster") + work := &workv1.ManifestWork{} + Eventually(func() error { + return runtimeClient.Get(ctx, types.NamespacedName{ + Name: workName, + Namespace: clusterName, + }, work) + }, timeout, interval).ShouldNot(HaveOccurred()) + + Expect(len(work.Spec.Workload.Manifests)).Should(Equal(8)) + + By("set InstallAgentOnLocal to false as a default value") + Eventually(func() bool { + err := runtimeClient.Get(ctx, mghLookupKey, existingMGH) + return err == nil + }, timeout, interval).Should(BeTrue()) + existingMGH.Spec.InstallAgentOnLocal = false + Expect(runtimeClient.Update(ctx, existingMGH)).Should(Succeed()) + }) }) diff --git a/test/integration/operator/controllers/agent/cluster_hosted_addon_test.go b/test/integration/operator/controllers/agent/cluster_hosted_addon_test.go index 7e9de51c0..c699f1a41 100644 --- a/test/integration/operator/controllers/agent/cluster_hosted_addon_test.go +++ b/test/integration/operator/controllers/agent/cluster_hosted_addon_test.go @@ -15,7 +15,7 @@ import ( "github.com/stolostron/multicluster-global-hub/pkg/constants" ) -// go test ./test/integration/operator/agent -ginkgo.focus "deploy hosted addon" -v +// go test ./test/integration/operator/controllers/agent -ginkgo.focus "deploy hosted addon" -v var _ = Describe("deploy hosted addon", func() { It("Should create hosted addon in OCP", func() { clusterName := fmt.Sprintf("hub-%s", rand.String(6)) // managed hub cluster -> enable local cluster diff --git a/test/integration/operator/controllers/agent/cluster_none_addon_test.go b/test/integration/operator/controllers/agent/cluster_none_addon_test.go index 2877023c6..1dec84127 100644 --- a/test/integration/operator/controllers/agent/cluster_none_addon_test.go +++ b/test/integration/operator/controllers/agent/cluster_none_addon_test.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" clusterv1 "open-cluster-management.io/api/cluster/v1" @@ -67,7 +68,7 @@ func prepareCluster(name string, labels, annotations map[string]string, })).Should(Succeed()) } -// go test ./test/integration/operator/agent -ginkgo.focus "none addon" -v +// go test ./test/integration/operator/controllers/agent -ginkgo.focus "none addon" -v var _ = Describe("none addon", func() { It("Should not create addon in these cases", func() { By("By preparing a non-OCP with deployMode label Managed Clusters") @@ -146,4 +147,23 @@ var _ = Describe("none addon", func() { return fmt.Errorf("check again %v", checkCount) }, timeout, interval).ShouldNot(HaveOccurred()) }) + + It("Should not create agent for the local-cluster", func() { + clusterName := fmt.Sprintf("hub-%s", rand.String(6)) + By("By preparing an OCP Managed Clusters") + prepareCluster(clusterName, + map[string]string{"vendor": "OpenShift", "local-cluster": "true"}, + map[string]string{}, + []clusterv1.ManagedClusterClaim{}, + clusterAvailableCondition) + + By("By checking the addon CR is is created in the cluster ns") + addon := &addonv1alpha1.ManagedClusterAddOn{} + Eventually(func() error { + return runtimeClient.Get(ctx, types.NamespacedName{ + Name: constants.GHManagedClusterAddonName, + Namespace: clusterName, + }, addon) + }, duration, interval).Should(HaveOccurred()) + }) }) diff --git a/test/integration/operator/controllers/agent/hosted_managedhub/mgh_hosted_test.go b/test/integration/operator/controllers/agent/hosted_managedhub/mgh_hosted_test.go index 568b9570c..ae52e8f9f 100644 --- a/test/integration/operator/controllers/agent/hosted_managedhub/mgh_hosted_test.go +++ b/test/integration/operator/controllers/agent/hosted_managedhub/mgh_hosted_test.go @@ -90,7 +90,7 @@ var hostedMGH = globalhubv1alpha4.MulticlusterGlobalHub{ // hosted: put the acm agent cluster control plane into acm hub cluster // global hub -> managed-hub(local-cluster) -// go test ./test/integration/operator/agent -ginkgo.focus "other addons in hosted mode test" -v +// go test ./test/integration/operator/controllers/agent -ginkgo.focus "other addons in hosted mode test" -v var _ = Describe("other addons in hosted mode test", Ordered, func() { var hostedAddonReconciler *agent.HostedAgentController BeforeAll(func() {