Skip to content

Commit

Permalink
Delete namespace when a gitRepo is deleted (rancher#2373)
Browse files Browse the repository at this point in the history
* Add geleteNamespace attribute to gitRepo

Introduce deleteNamespace to cleanup the namespace created to deploy
resources

* Add e2e tests for the deleteNamespace feature
  • Loading branch information
rubhanazeem authored May 7, 2024
1 parent fe4ccc9 commit e51e937
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 0 deletions.
20 changes: 20 additions & 0 deletions charts/fleet-crd/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ spec:
description: DeleteCRDResources deletes CRDs. Warning! this
will also delete all your Custom Resources.
type: boolean
deleteNamespace:
description: DeleteNamespace can be used to delete the deployed
namespace when removing the bundle
type: boolean
diff:
description: Diff can be used to ignore the modified state of
objects which are amended at runtime.
Expand Down Expand Up @@ -538,6 +542,10 @@ spec:
description: DeleteCRDResources deletes CRDs. Warning! this
will also delete all your Custom Resources.
type: boolean
deleteNamespace:
description: DeleteNamespace can be used to delete the deployed
namespace when removing the bundle
type: boolean
diff:
description: Diff can be used to ignore the modified state of
objects which are amended at runtime.
Expand Down Expand Up @@ -1284,6 +1292,10 @@ spec:
description: DeleteCRDResources deletes CRDs. Warning! this will
also delete all your Custom Resources.
type: boolean
deleteNamespace:
description: DeleteNamespace can be used to delete the deployed
namespace when removing the bundle
type: boolean
dependsOn:
description: DependsOn refers to the bundles which must be ready
before this bundle can be deployed.
Expand Down Expand Up @@ -2167,6 +2179,10 @@ spec:
description: DeleteCRDResources deletes CRDs. Warning! this
will also delete all your Custom Resources.
type: boolean
deleteNamespace:
description: DeleteNamespace can be used to delete the deployed
namespace when removing the bundle
type: boolean
diff:
description: Diff can be used to ignore the modified state
of objects which are amended at runtime.
Expand Down Expand Up @@ -5737,6 +5753,10 @@ spec:
in the helm history.
type: boolean
type: object
deleteNamespace:
description: DeleteNamespace specifies if the namespace created
must be deleted after deleting the GitRepo.
type: boolean
disablePolling:
description: Disables git polling. When enabled only webhooks will
be used.
Expand Down
11 changes: 11 additions & 0 deletions e2e/assets/single-cluster/delete-namespace/gitrepo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
kind: GitRepo
apiVersion: fleet.cattle.io/v1alpha1
metadata:
name: my-gitrepo
spec:
repo: https://github.com/rancher/fleet-test-data
branch: master
paths:
- helm-verify
targetNamespace: {{.TargetNamespace}}
deleteNamespace: {{.DeleteNamespace}}
110 changes: 110 additions & 0 deletions e2e/single-cluster/delete_namespaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package singlecluster_test

import (
"errors"
"strings"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rancher/fleet/e2e/testenv"
"github.com/rancher/fleet/e2e/testenv/kubectl"
)

var _ = Describe("delete namespaces", func() {
var (
k kubectl.Command
targetNamespace string
deleteNamespace bool
interval = 100 * time.Millisecond
duration = 2 * time.Second
)

type TemplateData struct {
TargetNamespace string
DeleteNamespace bool
}

BeforeEach(func() {
k = env.Kubectl.Namespace(env.Namespace)
deleteNamespace = false

DeferCleanup(func() {
_, _ = k.Delete("ns", "my-custom-namespace", "--wait=false")
})
})

JustBeforeEach(func() {
err := testenv.ApplyTemplate(k, testenv.AssetPath("single-cluster/delete-namespace/gitrepo.yaml"),
TemplateData{targetNamespace, deleteNamespace})

Expect(err).ToNot(HaveOccurred())
Eventually(func() error {
out, err := k.Namespace(targetNamespace).Get("configmaps")
if err != nil {
return err
}

if !strings.Contains(out, "app-config") {
return errors.New("expected configmap is not found")
}

return nil
}).ShouldNot(HaveOccurred())
})

When("delete namespaces is false", func() {
BeforeEach(func() {
targetNamespace = "my-custom-namespace"
})

It("preserves targetNamespace when GitRepo is deleted", func() {
out, err := k.Delete("gitrepo", "my-gitrepo", "-n", "fleet-local")
Expect(err).ToNot(HaveOccurred(), out)

Consistently(func() error {
_, err := k.Get("namespaces", targetNamespace)
return err
}, duration, interval).ShouldNot(HaveOccurred())
})
})

When("delete namespaces is true", func() {
BeforeEach(func() {
deleteNamespace = true
})

It("targetNamespace is deleted after deleting gitRepo", func() {
_, err := k.Get("namespaces", targetNamespace)
Expect(err).To(BeNil())

out, err := k.Delete("gitrepo", "my-gitrepo", "-n", "fleet-local")
Expect(err).ToNot(HaveOccurred(), out)

Eventually(func() error {
_, err = k.Get("namespaces", targetNamespace)
return err
}).ShouldNot(BeNil())
})
})

When("delete namespaces is true but resources are deployed in default namespace", func() {
BeforeEach(func() {
deleteNamespace = true
targetNamespace = "default"
})

It("default namespace exists", func() {
out, err := k.Delete("gitrepo", "my-gitrepo", "-n", "fleet-local")
Expect(err).ToNot(HaveOccurred(), out)

Eventually(func() string {
out, _ = k.Namespace(targetNamespace).Get("configmap", "app-config", "-o", "yaml")
return out
}).Should(ContainSubstring("Error from server (NotFound)"))

_, err = k.Get("namespaces", targetNamespace)
Expect(err).ToNot(HaveOccurred())
})
})
})
5 changes: 5 additions & 0 deletions internal/bundlereader/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Options struct {
Auth Auth
HelmRepoURLRegex string
KeepResources bool
DeleteNamespace bool
CorrectDrift *fleet.CorrectDrift
}

Expand Down Expand Up @@ -261,6 +262,10 @@ func read(ctx context.Context, name, baseDir string, bundleSpecReader io.Reader,
bundle.Spec.KeepResources = opts.KeepResources
}

if opts.DeleteNamespace {
bundle.Spec.DeleteNamespace = opts.DeleteNamespace
}

if opts.CorrectDrift != nil && opts.CorrectDrift.Enabled {
bundle.Spec.CorrectDrift = opts.CorrectDrift
}
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/cli/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Apply struct {
SSHPrivateKeyFile string `usage:"Path of ssh-private-key for helm repo" name:"ssh-privatekey-file"`
HelmRepoURLRegex string `usage:"Helm credentials will be used if the helm repo matches this regex. Credentials will always be used if this is empty or not provided" name:"helm-repo-url-regex"`
KeepResources bool `usage:"Keep resources created after the GitRepo or Bundle is deleted" name:"keep-resources"`
DeleteNamespace bool `usage:"Delete GitRepo target namespace after the GitRepo or Bundle is deleted" name:"delete-namespace"`
HelmCredentialsByPathFile string `usage:"Path of file containing helm credentials for paths" name:"helm-credentials-by-path-file"`
CorrectDrift bool `usage:"Rollback any change made from outside of Fleet" name:"correct-drift"`
CorrectDriftForce bool `usage:"Use --force when correcting drift. Resources can be deleted and recreated" name:"correct-drift-force"`
Expand Down Expand Up @@ -85,6 +86,7 @@ func (a *Apply) Run(cmd *cobra.Command, args []string) error {
SyncGeneration: int64(a.SyncGeneration),
HelmRepoURLRegex: a.HelmRepoURLRegex,
KeepResources: a.KeepResources,
DeleteNamespace: a.DeleteNamespace,
CorrectDrift: a.CorrectDrift,
CorrectDriftForce: a.CorrectDriftForce,
CorrectDriftKeepFailHistory: a.CorrectDriftKeepFailHistory,
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/cli/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Options struct {
Auth bundlereader.Auth
HelmRepoURLRegex string
KeepResources bool
DeleteNamespace bool
AuthByPath map[string]bundlereader.Auth
CorrectDrift bool
CorrectDriftForce bool
Expand Down Expand Up @@ -181,6 +182,7 @@ func readBundle(ctx context.Context, name, baseDir string, opts *Options) (*flee
Auth: opts.Auth,
HelmRepoURLRegex: opts.HelmRepoURLRegex,
KeepResources: opts.KeepResources,
DeleteNamespace: opts.DeleteNamespace,
CorrectDrift: &fleet.CorrectDrift{
Enabled: opts.CorrectDrift,
Force: opts.CorrectDriftForce,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ func argsAndEnvs(gitrepo *v1alpha1.GitRepo, debug bool) ([]string, []corev1.EnvV
args = append(args, "--keep-resources")
}

if gitrepo.Spec.DeleteNamespace {
args = append(args, "--delete-namespace")
}

if gitrepo.Spec.CorrectDrift != nil && gitrepo.Spec.CorrectDrift.Enabled {
args = append(args, "--correct-drift")
if gitrepo.Spec.CorrectDrift.Force {
Expand Down
54 changes: 54 additions & 0 deletions internal/cmd/controller/reconciler/gitrepo_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"context"
"fmt"
"reflect"
"slices"
"strings"

grutil "github.com/rancher/fleet/internal/cmd/controller/gitrepo"
"github.com/rancher/fleet/internal/cmd/controller/imagescan"
Expand All @@ -20,6 +22,7 @@ import (

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
errutil "k8s.io/apimachinery/pkg/util/errors"
Expand Down Expand Up @@ -285,6 +288,25 @@ func purgeBundles(ctx context.Context, c client.Client, gitrepo types.Namespaced
return err
}

// At this point, access to the GitRepo is unavailable as it has been deleted and cannot be found within the cluster.
// Nevertheless, `deleteNamespace` can be found within all bundles generated from that GitRepo. Checking any bundle to get this value would be enough.
namespace := ""
deleteNamespace := false
sampleBundle := fleet.Bundle{}
if len(bundles.Items) > 0 {
sampleBundle = bundles.Items[0]
deleteNamespace = sampleBundle.Spec.DeleteNamespace
namespace = sampleBundle.Spec.TargetNamespace

if sampleBundle.Spec.KeepResources {
deleteNamespace = false
}
}

if err = purgeNamespace(ctx, c, deleteNamespace, namespace); err != nil {
return err
}

for _, bundle := range bundles.Items {
err := c.Delete(ctx, &bundle) // nolint:gosec // does not store pointer
if err != nil {
Expand Down Expand Up @@ -335,6 +357,38 @@ func purgeImageScans(ctx context.Context, c client.Client, gitrepo types.Namespa
return nil
}

func purgeNamespace(ctx context.Context, c client.Client, deleteNamespace bool, ns string) error {
if !deleteNamespace {
return nil
}

if ns == "" {
return nil
}

// Ignore default namespaces
defaultNamespaces := []string{"fleet-local", "cattle-fleet-system", "fleet-default", "cattle-fleet-clusters-system", "default"}
if slices.Contains(defaultNamespaces, ns) {
return nil
}

// Ignore system namespaces
if _, isKubeNamespace := strings.CutPrefix(ns, "kube-"); isKubeNamespace {
return nil
}

namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
},
}
if err := c.Delete(ctx, namespace); err != nil {
return err
}

return nil
}

func acceptedLastUpdate(conds []genericcondition.GenericCondition) string {
for _, cond := range conds {
if cond.Type == "Accepted" {
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ type BundleDeploymentOptions struct {
// KeepResources can be used to keep the deployed resources when removing the bundle
KeepResources bool `json:"keepResources,omitempty"`

// DeleteNamespace can be used to delete the deployed namespace when removing the bundle
DeleteNamespace bool `json:"deleteNamespace,omitempty"`

//IgnoreOptions can be used to ignore fields when monitoring the bundle.
IgnoreOptions `json:"ignore,omitempty"`

Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/fleet.cattle.io/v1alpha1/gitrepo_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ type GitRepoSpec struct {
// KeepResources specifies if the resources created must be kept after deleting the GitRepo.
KeepResources bool `json:"keepResources,omitempty"`

// DeleteNamespace specifies if the namespace created must be deleted after deleting the GitRepo.
DeleteNamespace bool `json:"deleteNamespace,omitempty"`

// CorrectDrift specifies how drift correction should work.
CorrectDrift *CorrectDrift `json:"correctDrift,omitempty"`

Expand Down

0 comments on commit e51e937

Please sign in to comment.