From a7b1b17a48a43b0276efd532d14fcf2679c0b86e Mon Sep 17 00:00:00 2001 From: ccremer Date: Tue, 5 Jul 2022 15:13:40 +0200 Subject: [PATCH] Add appuio.io/organization label to namespace As required by APPUiO Cloud --- operator/standalone/provisioning.go | 1 + operator/steps/context.go | 3 +++ operator/steps/helmrelease_test.go | 2 +- operator/steps/namespace.go | 24 +++++++++++++++++++++++- operator/steps/namespace_it_test.go | 20 +++++++++++++++++++- 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/operator/standalone/provisioning.go b/operator/standalone/provisioning.go index 494d68c..9f09f3f 100644 --- a/operator/standalone/provisioning.go +++ b/operator/standalone/provisioning.go @@ -49,6 +49,7 @@ func (p *CreateStandalonePipeline) Run(ctx context.Context) error { return pipeline.NewPipeline(). WithSteps( pipeline.NewStepFromFunc("fetch operator config", steps.FetchOperatorConfigFn(p.operatorNamespace)), + pipeline.NewStepFromFunc("fetch instance namespace", steps.FetchNamespaceFn(instance.Namespace, steps.InstanceNamespaceKey{})), pipeline.NewStepFromFunc("add finalizer", steps.AddFinalizerFn(instance, finalizer)), pipeline.NewStepFromFunc("mark instance as progressing", steps.MarkInstanceAsProgressingFn()), diff --git a/operator/steps/context.go b/operator/steps/context.go index 0527dcc..074ac78 100644 --- a/operator/steps/context.go +++ b/operator/steps/context.go @@ -37,6 +37,9 @@ type ConnectionSecretKey struct{} // CredentialSecretKey identifies the credential secret for PostgreSQL in the context. type CredentialSecretKey struct{} +// InstanceNamespaceKey identifies the namespace resource of the instance in the context. +type InstanceNamespaceKey struct{} + // SetClientInContext sets the given client in the context. func SetClientInContext(ctx context.Context, c client.Client) { pipeline.StoreInContext(ctx, ClientKey{}, c) diff --git a/operator/steps/helmrelease_test.go b/operator/steps/helmrelease_test.go index e4f2e1f..0c0b60d 100644 --- a/operator/steps/helmrelease_test.go +++ b/operator/steps/helmrelease_test.go @@ -166,7 +166,7 @@ func TestApplyValuesFromInstance(t *testing.T) { assert.Equal(t, testValues, result) } -func TestCreateStandalonePipeline_IsHelmReleaseReady(t *testing.T) { +func TestIsHelmReleaseReady(t *testing.T) { // Arrange ctx := pipeline.MutableContext(context.Background()) instance := newInstance("release-ready", "my-app") diff --git a/operator/steps/namespace.go b/operator/steps/namespace.go index dc18a5f..69be664 100644 --- a/operator/steps/namespace.go +++ b/operator/steps/namespace.go @@ -6,20 +6,30 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) +// AppuioOrganizationLabelKey is the label key required for setting ownership of a namespace +const AppuioOrganizationLabelKey = "appuio.io/organization" + // EnsureNamespace creates the namespace with given name and labels. func EnsureNamespace(name string, labelSet labels.Set) func(ctx context.Context) error { return func(ctx context.Context) error { kube := GetClientFromContext(ctx) + instanceNamespace := getFromContextOrPanic(ctx, InstanceNamespaceKey{}).(*corev1.Namespace) + instanceNsLabels := instanceNamespace.Labels + copyLabels := labels.Set{} + if org, exists := instanceNsLabels[AppuioOrganizationLabelKey]; exists { + copyLabels[AppuioOrganizationLabelKey] = org + } deploymentNamespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: name}, } _, err := controllerutil.CreateOrUpdate(ctx, kube, deploymentNamespace, func() error { - deploymentNamespace.Labels = labels.Merge(deploymentNamespace.Labels, labelSet) + deploymentNamespace.Labels = labels.Merge(deploymentNamespace.Labels, labels.Merge(copyLabels, labelSet)) return nil }) pipeline.StoreInContext(ctx, DeploymentNamespaceKey{}, deploymentNamespace) @@ -51,3 +61,15 @@ func DeleteNamespaceFn() func(ctx context.Context) error { return client.IgnoreNotFound(err) } } + +// FetchNamespaceFn fetches the namespace of the given name and stores it in the context with given key. +func FetchNamespaceFn(namespaceName string, contextKey any) func(ctx context.Context) error { + return func(ctx context.Context) error { + kube := GetClientFromContext(ctx) + + ns := &corev1.Namespace{} + err := kube.Get(ctx, types.NamespacedName{Name: namespaceName}, ns) + pipeline.StoreInContext(ctx, contextKey, ns) + return err + } +} diff --git a/operator/steps/namespace_it_test.go b/operator/steps/namespace_it_test.go index e5db846..57a5909 100644 --- a/operator/steps/namespace_it_test.go +++ b/operator/steps/namespace_it_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/vshn/appcat-service-postgresql/operator/operatortest" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "testing" @@ -24,10 +25,12 @@ func (ts *NamespaceSuite) BeforeTest(suiteName, testName string) { SetClientInContext(ts.Context, ts.Client) } -func (ts *NamespaceSuite) Test_EnsureDeploymentNamespace() { +func (ts *NamespaceSuite) Test_EnsureNamespace() { // Arrange instance := newInstance("test-ensure-namespace", "my-app") + instanceNs := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: instance.Namespace, Labels: map[string]string{"appuio.io/organization": "organization"}}} SetInstanceInContext(ts.Context, instance) + pipeline.StoreInContext(ts.Context, InstanceNamespaceKey{}, instanceNs) // Act err := EnsureNamespace("sv-postgresql-s-merry-vigilante-7b16", labels.Set{ @@ -41,6 +44,7 @@ func (ts *NamespaceSuite) Test_EnsureDeploymentNamespace() { ts.FetchResource(types.NamespacedName{Name: "sv-postgresql-s-merry-vigilante-7b16"}, ns) ts.Assert().Equal(ns.Labels["app.kubernetes.io/instance"], instance.Name) ts.Assert().Equal(ns.Labels["app.kubernetes.io/instance-namespace"], instance.Namespace) + ts.Assert().Equal(ns.Labels["appuio.io/organization"], "organization", "label required by APPUiO Cloud") } func (ts *NamespaceSuite) Test_DeleteNamespace() { @@ -79,3 +83,17 @@ func (ts *NamespaceSuite) Test_DeleteNamespace() { }) } } + +func (ts *NamespaceSuite) Test_FetchNamespaceFn() { + // Arrange + ns := "fetch-ns" + ts.EnsureNS(ns) + + // Act + err := FetchNamespaceFn(ns, InstanceNamespaceKey{})(ts.Context) + ts.Require().NoError(err) + + // Assert + result := getFromContextOrPanic(ts.Context, InstanceNamespaceKey{}).(*corev1.Namespace) + ts.Assert().Equal(ns, result.Name, "ns name") +}