Skip to content

Commit

Permalink
feat: add bash script for uninstalling ACM (#1453)
Browse files Browse the repository at this point in the history
This is an alternative approach to uninstalling Config Management and
the operator. This is intended as a simple/portable alternative to using
the nomos migrate command.
  • Loading branch information
sdowell authored Oct 17, 2024
1 parent 5162ba9 commit c26bb4a
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions build/prow/e2e/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
!/scripts
!/e2e
!/examples
!/installation
169 changes: 169 additions & 0 deletions e2e/testcases/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1709,3 +1709,172 @@ func TestNomosMigrateMonoRepo(t *testing.T) {
nt.T.Log("Waiting for RootSync to be synced")
nt.Must(nt.WatchForAllSyncs(nomostest.SkipReadyCheck()))
}

// This test case validates the behavior of the uninstall script defined
// at installation/uninstall_configmanagement.sh
func TestACMUninstallScript(t *testing.T) {
nt := nomostest.New(t, nomostesting.NomosCLI, ntopts.SkipConfigSyncInstall)

nt.T.Cleanup(func() {
// Restore state of Config Sync installation after test
if err := nomostest.InstallConfigSync(nt); err != nil {
nt.T.Fatal(err)
}
})
nt.T.Cleanup(func() {
cmObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "configmanagement.gke.io/v1",
"kind": "ConfigManagement",
"metadata": map[string]interface{}{
"name": "config-management",
},
},
}

if err := nt.KubeClient.MergePatch(cmObj, `{"metadata":{"finalizers":[]}}`); err != nil {
if apierrors.IsNotFound(err) {
return // object already deleted, exit early to prevent watch error (GVK not found)
}
nt.T.Error(err)
}
// Delete the ConfigManagement CR, in case the test failed early.
if err := nt.KubeClient.Delete(cmObj); err != nil {
if apierrors.IsNotFound(err) {
return // object already deleted, exit early to prevent watch error (GVK not found)
}
nt.T.Fatal(err)
}
if err := nt.Watcher.WatchForNotFound(kinds.ConfigManagement(), cmObj.GetName(), "", testwatcher.WatchUnstructured()); err != nil {
nt.T.Fatal(err)
}
})
nt.T.Cleanup(func() {
if t.Failed() {
nt.PodLogs(configsync.ControllerNamespace, util.ACMOperatorDeployment, "", false)
}
// Delete the ConfigManagement operator in case the test failed early.
// If this lingers around it could cause issues for subsequent tests.
cmDeployment := k8sobjects.DeploymentObject(
core.Namespace(configsync.ControllerNamespace),
core.Name(util.ACMOperatorDeployment),
)
if err := nt.KubeClient.Delete(cmDeployment, client.PropagationPolicy(metav1.DeletePropagationForeground)); err != nil && !apierrors.IsNotFound(err) {
nt.T.Fatal(err)
}
if err := nt.Watcher.WatchForNotFound(kinds.Deployment(), util.ACMOperatorDeployment, configsync.ControllerNamespace); err != nil {
nt.T.Error(err)
}
})

nt.T.Log("Installing ConfigManagement")
nt.MustKubectl("apply", "-f", "../testdata/configmanagement/1.18.0/config-management-operator.yaml")

tg := taskgroup.New()
tg.Go(func() error {
return nt.Watcher.WatchForCurrentStatus(kinds.CustomResourceDefinitionV1(), util.ConfigManagementCRDName, "")
})
tg.Go(func() error {
return nt.Watcher.WatchForCurrentStatus(kinds.Deployment(), util.ACMOperatorDeployment, configsync.ControllerNamespace)
})
nt.Must(tg.Wait())

cmObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "configmanagement.gke.io/v1",
"kind": "ConfigManagement",
"metadata": map[string]interface{}{
"name": "config-management",
},
"spec": map[string]interface{}{
"enableMultiRepo": true,
"hierarchyController": map[string]interface{}{
"enabled": true,
},
},
},
}

nt.T.Log("Configuring ConfigManagement for multi-repo mode")
if err := nt.KubeClient.Create(cmObj); err != nil {
nt.T.Fatal(err)
}

nt.Must(nt.Watcher.WatchObject(kinds.Deployment(), "reconciler-manager", configsync.ControllerNamespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentContainerImageEquals(
"reconciler-manager",
"gcr.io/config-management-release/reconciler-manager:v1.18.0-rc.3",
),
)))

nt.T.Log("Running uninstall script should fail when HNC is enabled")
out, err := nt.Shell.Command("./../../installation/uninstall_configmanagement.sh").CombinedOutput()
if err == nil {
nt.T.Fatal("Expected uninstall script to return non-zero exit code")
}
assert.Contains(t, string(out), "Hierarchy Controller is enabled on the ConfigManagement object. It must be disabled before migrating.")

nt.T.Log("Disabling HNC")
cmObj = &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "configmanagement.gke.io/v1",
"kind": "ConfigManagement",
"metadata": map[string]interface{}{
"name": "config-management",
},
"spec": map[string]interface{}{
"enableMultiRepo": true,
"hierarchyController": map[string]interface{}{
"enabled": false,
},
},
},
}
nt.Must(nt.KubeClient.Apply(cmObj))
nt.Must(nt.Watcher.WatchForNotFound(kinds.Namespace(), "hnc-system", ""))
nt.Must(nt.Validate(util.ACMOperatorDeployment, configsync.ControllerNamespace, k8sobjects.DeploymentObject()))

nt.T.Log("Running uninstall script to remove ACM operator")
nt.Must(nt.Shell.Command("./../../installation/uninstall_configmanagement.sh").CombinedOutput())

nt.T.Log("Wait for legacy resources to be NotFound...")
tg = taskgroup.New()
tg.Go(func() error {
return nt.Watcher.WatchForNotFound(kinds.Deployment(),
util.ACMOperatorDeployment, configsync.ControllerNamespace)
})
tg.Go(func() error {
return nt.Watcher.WatchForNotFound(kinds.Deployment(),
"git-importer", configsync.ControllerNamespace)
})
tg.Go(func() error {
return nt.Watcher.WatchForNotFound(kinds.CustomResourceDefinitionV1(),
util.ConfigManagementCRDName, "")
})
if err := tg.Wait(); err != nil {
nt.T.Fatal(err)
}

nt.T.Log("Wait for expected resources to be current...")
tg = taskgroup.New()
tg.Go(func() error {
return nt.Watcher.WatchForCurrentStatus(kinds.Deployment(),
util.ReconcilerManagerName, configsync.ControllerNamespace)
})
tg.Go(func() error {
return nt.Watcher.WatchForCurrentStatus(kinds.Deployment(),
configmanagement.RGControllerName, configmanagement.RGControllerNamespace)
})
tg.Go(func() error {
return nt.Watcher.WatchForNotFound(kinds.Deployment(),
core.RootReconcilerName(configsync.RootSyncName), configsync.ControllerNamespace)
})
tg.Go(func() error {
return nt.Watcher.WatchForNotFound(kinds.RootSyncV1Beta1(),
configsync.RootSyncName, configsync.ControllerNamespace)
})
if err := tg.Wait(); err != nil {
nt.T.Fatal(err)
}
}
38 changes: 38 additions & 0 deletions installation/uninstall_configmanagement.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This script uninstalls ConfigManagement

set -euox pipefail

hnc_enabled="$(kubectl get configmanagements.configmanagement.gke.io config-management -o=jsonpath="{.spec.hierarchyController.enabled}" --ignore-not-found)"

if [[ "${hnc_enabled}" == "true" ]]; then
echo "Hierarchy Controller is enabled on the ConfigManagement object. It must be disabled before migrating."
echo "This can be done by unsetting the spec.hierarchyController field on ConfigManagement."
exit 1
fi

kubectl delete deployment -n config-management-system config-management-operator --ignore-not-found --cascade=foreground

if kubectl get configmanagement config-management &> /dev/null ; then
kubectl patch configmanagement config-management --type="merge" -p '{"metadata":{"finalizers":[]}}'
kubectl delete configmanagement config-management --cascade=orphan --ignore-not-found
fi

kubectl delete clusterrolebinding config-management-operator --ignore-not-found
kubectl delete clusterrole config-management-operator --ignore-not-found
kubectl delete serviceaccount -n config-management-system config-management-operator --ignore-not-found
kubectl delete customresourcedefinition configmanagements.configmanagement.gke.io --ignore-not-found

0 comments on commit c26bb4a

Please sign in to comment.