diff --git a/Makefile b/Makefile index e81b5102..ea3555cd 100644 --- a/Makefile +++ b/Makefile @@ -86,7 +86,7 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager main.go + go build -o bin/manager cmd/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. diff --git a/README.md b/README.md index b8a80a9a..52dd2da2 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,25 @@ make run > **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin privileges or be logged in as admin. +## Namespace Prefix Configuration + +The operator creates namespaces for each stack with the format `-`. You can configure a prefix to be added to all namespace names by using the `--namespace-prefix` command line flag. + +**Example:** +- Without prefix: `myorg-mystack` +- With prefix `dev-`: `dev-myorg-mystack` + +**Command line usage:** +```sh +make run -- --namespace-prefix=dev- +``` + +**Helm chart usage:** +```yaml +operator: + namespacePrefix: "dev-" +``` + **Create instances of your solution** You can apply the samples (examples) from the config/sample: diff --git a/cmd/main.go b/cmd/main.go index ab504281..e3d71518 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -67,6 +67,7 @@ func main() { env string licenceSecret string utilsVersion string + namespacePrefix string ) flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -77,6 +78,7 @@ func main() { flag.StringVar(&env, "env", "staging", "The current environment in use for the operator") flag.StringVar(&licenceSecret, "licence-secret", "", "The licence secret that contains the token and the issuer") flag.StringVar(&utilsVersion, "utils-version", "latest", "The version of the operator utils image") + flag.StringVar(&namespacePrefix, "namespace-prefix", "", "The prefix to use when creating namespaces (format: -)") opts := zap.Options{ Development: false, } @@ -119,10 +121,11 @@ func main() { } platform := core.Platform{ - Region: region, - Environment: env, - LicenceSecret: licenceSecret, - UtilsVersion: utilsVersion, + Region: region, + Environment: env, + LicenceSecret: licenceSecret, + UtilsVersion: utilsVersion, + NamespacePrefix: namespacePrefix, } if err := core.Setup(mgr, platform); err != nil { diff --git a/helm/operator/templates/deployment.yaml b/helm/operator/templates/deployment.yaml index 25b63113..29e1743c 100644 --- a/helm/operator/templates/deployment.yaml +++ b/helm/operator/templates/deployment.yaml @@ -52,6 +52,9 @@ spec: {{- with .Values.operator.region }} - --region={{ $.Values.operator.region }} {{- end }} + {{- with .Values.operator.namespacePrefix }} + - --namespace-prefix={{ $.Values.operator.namespacePrefix }} + {{- end }} {{- if and .Values.global.licence.createSecret (eq (len .Values.global.licence.existingSecret) 0) }} - --licence-secret={{ include "operator.fullname" . }}-licence {{- else }} diff --git a/helm/operator/values.yaml b/helm/operator/values.yaml index 1c3e8d54..b89ff011 100644 --- a/helm/operator/values.yaml +++ b/helm/operator/values.yaml @@ -48,6 +48,8 @@ operator: probeAddr: ":8081" # Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. enableLeaderElection: true + # The namespace prefix to use when creating namespaces (format: -) + namespacePrefix: "" utils: tag: "" diff --git a/internal/core/names.go b/internal/core/names.go index 365231c3..b0a86590 100644 --- a/internal/core/names.go +++ b/internal/core/names.go @@ -22,3 +22,20 @@ func GetResourceName(name string) types.NamespacedName { Name: name, } } + +// GetNamespaceName generates a namespace name with optional prefix +// Format: - or - if no prefix +func GetNamespaceName(platform Platform, stackName string) string { + if platform.NamespacePrefix == "" { + return stackName + } + return platform.NamespacePrefix + stackName +} + +// GetNamespacedResourceNameWithPrefix generates a namespaced resource name using the prefixed namespace +func GetNamespacedResourceNameWithPrefix(platform Platform, stackName, name string) types.NamespacedName { + return types.NamespacedName{ + Namespace: GetNamespaceName(platform, stackName), + Name: name, + } +} diff --git a/internal/core/platform.go b/internal/core/platform.go index f11c498b..8b54a6af 100644 --- a/internal/core/platform.go +++ b/internal/core/platform.go @@ -10,4 +10,6 @@ type Platform struct { LicenceSecret string // The operator utils image version UtilsVersion string + // The namespace prefix to use when creating namespaces + NamespacePrefix string } diff --git a/internal/resources/benthos/controller.go b/internal/resources/benthos/controller.go index 477c2f87..0eeb36d7 100644 --- a/internal/resources/benthos/controller.go +++ b/internal/resources/benthos/controller.go @@ -213,7 +213,7 @@ func createDeployment(ctx Context, stack *v1beta1.Stack, b *v1beta1.Benthos) err object := &unstructured.Unstructured{} object.SetGroupVersionKind(kinds[0]) - object.SetNamespace(stack.Name) + object.SetNamespace(GetNamespaceName(ctx.GetPlatform(), stack.Name)) object.SetName("benthos-audit") if err := client.IgnoreNotFound(ctx.GetClient().Delete(ctx, object)); err != nil { return errors.Wrap(err, "deleting audit config map") diff --git a/internal/resources/ledgers/deployments.go b/internal/resources/ledgers/deployments.go index c3127085..9439b88d 100644 --- a/internal/resources/ledgers/deployments.go +++ b/internal/resources/ledgers/deployments.go @@ -66,7 +66,7 @@ func hasDeploymentStrategyChanged(ctx core.Context, stack *v1beta1.Stack, ledger case v1beta1.DeploymentStrategySingle: return uninstallLedgerMonoWriterMultipleReader(ctx, stack) case v1beta1.DeploymentStrategyMonoWriterMultipleReader: - return core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceName(stack.Name, "ledger")) + return core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, "ledger")) default: return fmt.Errorf("unknown deployment strategy %s", strategy) } @@ -385,10 +385,10 @@ func installLedgerMonoWriterMultipleReader(ctx core.Context, stack *v1beta1.Stac func uninstallLedgerMonoWriterMultipleReader(ctx core.Context, stack *v1beta1.Stack) error { remove := func(name string) error { - if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceName(stack.Name, name)); err != nil { + if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, name)); err != nil { return err } - if err := core.DeleteIfExists[*corev1.Service](ctx, core.GetNamespacedResourceName(stack.Name, name)); err != nil { + if err := core.DeleteIfExists[*corev1.Service](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, name)); err != nil { return err } @@ -403,7 +403,7 @@ func uninstallLedgerMonoWriterMultipleReader(ctx core.Context, stack *v1beta1.St return err } - if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceName(stack.Name, "ledger-gateway")); err != nil { + if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, "ledger-gateway")); err != nil { return err } diff --git a/internal/resources/payments/deployments.go b/internal/resources/payments/deployments.go index 00bacaae..f03d661a 100644 --- a/internal/resources/payments/deployments.go +++ b/internal/resources/payments/deployments.go @@ -166,10 +166,10 @@ func commonEnvVars(ctx core.Context, stack *v1beta1.Stack, payments *v1beta1.Pay func uninstallPaymentsReadAndConnectors(ctx core.Context, stack *v1beta1.Stack) error { remove := func(name string) error { - if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceName(stack.Name, name)); err != nil { + if err := core.DeleteIfExists[*appsv1.Deployment](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, name)); err != nil { return err } - if err := core.DeleteIfExists[*corev1.Service](ctx, core.GetNamespacedResourceName(stack.Name, name)); err != nil { + if err := core.DeleteIfExists[*corev1.Service](ctx, core.GetNamespacedResourceNameWithPrefix(ctx.GetPlatform(), stack.Name, name)); err != nil { return err } diff --git a/internal/resources/resourcereferences/init.go b/internal/resources/resourcereferences/init.go index 90b99e25..a1675d63 100644 --- a/internal/resources/resourcereferences/init.go +++ b/internal/resources/resourcereferences/init.go @@ -110,7 +110,7 @@ func Reconcile(ctx core.Context, stack *v1beta1.Stack, req *v1beta1.ResourceRefe oldResource := &unstructured.Unstructured{} oldResource.SetGroupVersionKind(gvk) err := ctx.GetClient().Get(ctx, types.NamespacedName{ - Namespace: stack.Name, + Namespace: core.GetNamespaceName(ctx.GetPlatform(), stack.Name), Name: req.Status.SyncedResource, }, oldResource) if client.IgnoreNotFound(err) != nil { @@ -147,7 +147,7 @@ func Reconcile(ctx core.Context, stack *v1beta1.Stack, req *v1beta1.ResourceRefe newResource := &unstructured.Unstructured{} newResource.SetGroupVersionKind(gvk) - newResource.SetNamespace(stack.Name) + newResource.SetNamespace(core.GetNamespaceName(ctx.GetPlatform(), stack.Name)) newResource.SetName(req.Spec.Name) _, err = controllerutil.CreateOrUpdate(ctx, ctx.GetClient(), newResource, func() error { diff --git a/internal/resources/stacks/init.go b/internal/resources/stacks/init.go index 1b5ef816..f2c92941 100644 --- a/internal/resources/stacks/init.go +++ b/internal/resources/stacks/init.go @@ -201,9 +201,13 @@ func Reconcile(ctx Context, stack *v1beta1.Stack) error { logger := log.FromContext(ctx) logger = logger.WithValues("stack", stack.Name) logger.Info("Reconcile stack") + + // Generate namespace name with optional prefix + namespaceName := GetNamespaceName(ctx.GetPlatform(), stack.Name) + if _, _, err := CreateOrUpdate(ctx, types.NamespacedName{ - Name: stack.Name, + Name: namespaceName, }, namespaceLabel(ctx, stack.Name), namespaceAnnotations(ctx, stack.Name),