Skip to content

Commit

Permalink
feat: Ability to create namespace by specifying namespace object
Browse files Browse the repository at this point in the history
This also adds tests to ensure that SSA is adhered to properly in this case.
  • Loading branch information
jimmidyson authored and dlipovetsky committed Apr 25, 2024
1 parent f32deb6 commit 808dae7
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (s crsStrategy) apply(
if err != nil {
return fmt.Errorf("error creating remote cluster client: %w", err)
}
if err = utils.EnsureNamespace(ctx, remoteClient, cluster.Namespace); err != nil {
if err = utils.EnsureNamespaceWithName(ctx, remoteClient, cluster.Namespace); err != nil {
return fmt.Errorf(
"failed to create Namespace in remote cluster: %w",
err,
Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/generic/lifecycle/utils/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func CopySecretToRemoteCluster(
return fmt.Errorf("error creating client for remote cluster: %w", err)
}

err = EnsureNamespace(ctx, remoteClient, dstSecretKey.Namespace)
err = EnsureNamespaceWithName(ctx, remoteClient, dstSecretKey.Namespace)
if err != nil {
return fmt.Errorf("error creating namespace on the remote cluster: %w", err)
}
Expand Down
15 changes: 13 additions & 2 deletions pkg/handlers/generic/lifecycle/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func EnsureCRSForClusterFromObjects(
return nil
}

// EnsureNamespace will create the namespace if it does not exist.
func EnsureNamespace(ctx context.Context, c ctrlclient.Client, name string) error {
// EnsureNamespaceWithName will create the namespace with the specified name if it does not exist.
func EnsureNamespaceWithName(ctx context.Context, c ctrlclient.Client, name string) error {
ns := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Expand All @@ -105,6 +105,17 @@ func EnsureNamespace(ctx context.Context, c ctrlclient.Client, name string) erro
},
}

return EnsureNamespace(ctx, c, ns)
}

// EnsureNamespace will create the namespace if it does not exist.
func EnsureNamespace(ctx context.Context, c ctrlclient.Client, ns *corev1.Namespace) error {
if ns.TypeMeta.APIVersion == "" {
ns.TypeMeta.APIVersion = corev1.SchemeGroupVersion.String()
}
if ns.TypeMeta.Kind == "" {
ns.TypeMeta.Kind = "Namespace"
}
err := client.ServerSideApply(ctx, c, ns)
if err != nil {
return fmt.Errorf("failed to server side apply %w", err)
Expand Down
85 changes: 60 additions & 25 deletions pkg/handlers/generic/lifecycle/utils/utils_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/storage/names"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/test/helpers"
Expand All @@ -18,45 +19,79 @@ var _ = Describe("Namespace", func() {
c, err := helpers.TestEnv.GetK8sClient()
Expect(err).To(BeNil())

namespaceName := "new"
namespaceName := names.SimpleNameGenerator.GenerateName("test-")

Expect(EnsureNamespace(ctx, c, namespaceName)).To(Succeed())
Expect(EnsureNamespaceWithName(ctx, c, namespaceName)).To(Succeed())
Expect(c.Delete(ctx, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
})).To((Succeed()))
})

It("updates a namespace, preserving user-managed fields", func(ctx SpecContext) {
c, err := helpers.TestEnv.GetK8sClient()
Expect(err).To(BeNil())
It(
"updates a namespace with no changes, preserving user-managed fields",
func(ctx SpecContext) {
c, err := helpers.TestEnv.GetK8sClient()
Expect(err).To(BeNil())

namespaceName := names.SimpleNameGenerator.GenerateName("test-")
Expect(c.Create(ctx,
&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
Labels: map[string]string{
"userkey": "uservalue",
},
},
})).To(Succeed())

namespaceName := "existing"
Expect(c.Create(ctx,
&corev1.Namespace{
Expect(EnsureNamespaceWithName(ctx, c, namespaceName)).To(Succeed())

ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
Expect(c.Get(ctx, client.ObjectKeyFromObject(ns), ns)).To(Succeed())
Expect(ns.GetLabels()).To(HaveKeyWithValue("userkey", "uservalue"))
},
)

It(
"updates a namespace by only sending new labels, preserving existing user-managed fields",
func(ctx SpecContext) {
c, err := helpers.TestEnv.GetK8sClient()
Expect(err).To(BeNil())

namespaceName := names.SimpleNameGenerator.GenerateName("test-")
Expect(c.Create(ctx,
&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
Labels: map[string]string{
"userkey": "uservalue",
},
},
})).To(Succeed())

Expect(EnsureNamespace(ctx, c, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
Labels: map[string]string{
"userkey": "uservalue",
"newkey": "newvalue",
},
},
})).To(Succeed())

Expect(EnsureNamespace(ctx, c, namespaceName)).To(Succeed())

ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
Expect(c.Get(ctx, client.ObjectKeyFromObject(ns), ns)).To(Succeed())
Expect(ns.GetLabels()).To(HaveKeyWithValue("userkey", "uservalue"))

Expect(c.Delete(ctx, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
})).To((Succeed()))
})
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
Expect(c.Get(ctx, client.ObjectKeyFromObject(ns), ns)).To(Succeed())
Expect(ns.GetLabels()).To(HaveKeyWithValue("userkey", "uservalue"))
Expect(ns.GetLabels()).To(HaveKeyWithValue("newkey", "newvalue"))
},
)
})

0 comments on commit 808dae7

Please sign in to comment.