From 2a3c8b208d0cbd71dd34a506b695ec8cd71528ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Irving=20Mondrag=C3=B3n?= Date: Sat, 14 Mar 2026 22:59:15 +0100 Subject: [PATCH] feat(api): soft-deprecate spec.targetNamespace field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Irving Mondragón --- .golangci.yml | 8 ++++ README.md | 7 ++- api/v1alpha1/shipwrightbuild_types.go | 2 + ...erator.shipwright.io_shipwrightbuilds.yaml | 6 ++- ...wright-operator.clusterserviceversion.yaml | 8 ++-- ...erator.shipwright.io_shipwrightbuilds.yaml | 6 ++- config/manager/manager.yaml | 4 ++ .../operator_v1alpha1_shipwrightbuild.yaml | 3 +- controllers/shipwrightbuild_controller.go | 46 +++++++++---------- .../shipwrightbuild_controller_test.go | 7 ++- controllers/shipwrightbuild_rbac.go | 1 + controllers/suite_test.go | 1 + docs/shipwrightbuild.md | 2 +- main.go | 8 ++++ pkg/common/const.go | 5 ++ pkg/common/util.go | 5 ++ 16 files changed, 79 insertions(+), 40 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d2a3cda2e..5dcd7b559 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,3 +2,11 @@ version: "2" run: timeout: 10m + +linters: + exclusions: + rules: + # TODO(#241): Remove once TargetNamespace field is fully removed + - linters: + - staticcheck + text: "SA1019:.*TargetNamespace" diff --git a/README.md b/README.md index 6171bfc00..8b49c8c20 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,11 @@ apiVersion: operator.shipwright.io/v1alpha1 kind: ShipwrightBuild metadata: name: shipwright-operator -spec: - targetNamespace: shipwright-build +spec: {} ``` -The operator will deploy Shipwright Builds in the provided `targetNamespace`. -When `.spec.targetNamespace` is not set, the namespace will default to `shipwright-build`. +The operator will deploy Shipwright Builds in the operator's own namespace by default. +The `.spec.targetNamespace` field is **deprecated** — if set, it is still honored but logs a deprecation warning. It will be removed in a future release. To deploy [Shipwright Triggers](https://github.com/shipwright-io/triggers), add the `triggers` field: diff --git a/api/v1alpha1/shipwrightbuild_types.go b/api/v1alpha1/shipwrightbuild_types.go index 3e7c87453..f5d65905a 100644 --- a/api/v1alpha1/shipwrightbuild_types.go +++ b/api/v1alpha1/shipwrightbuild_types.go @@ -34,6 +34,8 @@ type TriggersSpec struct { // ShipwrightBuildSpec defines the configuration of a Shipwright Build deployment. type ShipwrightBuildSpec struct { // TargetNamespace is the target namespace where Shipwright's build controller will be deployed. + // + // Deprecated: will be removed in a future release. Defaults to the operator's namespace. TargetNamespace string `json:"targetNamespace,omitempty"` // Triggers configures the deployment of the Shipwright Triggers component. diff --git a/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml b/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml index 5491dd09a..91d450018 100644 --- a/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml +++ b/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml @@ -42,8 +42,10 @@ spec: Build deployment. properties: targetNamespace: - description: TargetNamespace is the target namespace where Shipwright's - build controller will be deployed. + description: |- + TargetNamespace is the target namespace where Shipwright's build controller will be deployed. + + Deprecated: will be removed in a future release. Defaults to the operator's namespace. type: string triggers: description: |- diff --git a/bundle/manifests/shipwright-operator.clusterserviceversion.yaml b/bundle/manifests/shipwright-operator.clusterserviceversion.yaml index b4497db9d..16605a28a 100644 --- a/bundle/manifests/shipwright-operator.clusterserviceversion.yaml +++ b/bundle/manifests/shipwright-operator.clusterserviceversion.yaml @@ -10,9 +10,7 @@ metadata: "metadata": { "name": "shipwright-build" }, - "spec": { - "targetNamespace": "shipwright-build" - } + "spec": {} } ] capabilities: Basic Install @@ -682,6 +680,10 @@ spec: env: - name: USE_MANAGED_WEBHOOK_CERTS value: "true" + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace image: ko://github.com/shipwright-io/operator livenessProbe: httpGet: diff --git a/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml b/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml index 862dc642d..afe9595fe 100644 --- a/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml +++ b/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml @@ -42,8 +42,10 @@ spec: Build deployment. properties: targetNamespace: - description: TargetNamespace is the target namespace where Shipwright's - build controller will be deployed. + description: |- + TargetNamespace is the target namespace where Shipwright's build controller will be deployed. + + Deprecated: will be removed in a future release. Defaults to the operator's namespace. type: string triggers: description: |- diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 0e1da47d2..f658e4fe3 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -35,6 +35,10 @@ spec: env: - name: USE_MANAGED_WEBHOOK_CERTS value: "true" + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace image: ko://github.com/shipwright-io/operator name: manager securityContext: diff --git a/config/samples/operator_v1alpha1_shipwrightbuild.yaml b/config/samples/operator_v1alpha1_shipwrightbuild.yaml index 42db15567..0199e6e81 100644 --- a/config/samples/operator_v1alpha1_shipwrightbuild.yaml +++ b/config/samples/operator_v1alpha1_shipwrightbuild.yaml @@ -2,5 +2,4 @@ apiVersion: operator.shipwright.io/v1alpha1 kind: ShipwrightBuild metadata: name: shipwright-build -spec: - targetNamespace: shipwright-build +spec: {} diff --git a/controllers/shipwrightbuild_controller.go b/controllers/shipwrightbuild_controller.go index c9b3ef704..db15e58b8 100644 --- a/controllers/shipwrightbuild_controller.go +++ b/controllers/shipwrightbuild_controller.go @@ -38,8 +38,6 @@ import ( const ( // FinalizerAnnotation annotation string appended on finalizer slice. FinalizerAnnotation = "finalizer.operator.shipwright.io" - // defaultTargetNamespace fallback namespace when `.spec.namepace` is not informed. - defaultTargetNamespace = "shipwright-build" // Ready object is providing service. ConditionReady = "Ready" @@ -64,6 +62,7 @@ type ShipwrightBuildReconciler struct { TektonManifest manifestival.Manifest // Tekton release manifest render BuildStrategyManifest manifestival.Manifest // Build strategies manifest to render TriggersManifest manifestival.Manifest // Triggers manifest to render + OperatorNamespace string // namespace the operator is running in } type TektonCheckResult struct { @@ -235,34 +234,33 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ return Requeue() } - // selecting the target namespace based on the CRD information, when not informed using the - // default namespace instead targetNamespace := b.Spec.TargetNamespace - if targetNamespace == "" { - logger.Info( - "Namespace is not informed! Target namespace is selected from default settings instead", - "defaultTargetNamespace", defaultTargetNamespace, + if targetNamespace != "" && targetNamespace != r.OperatorNamespace { + logger.Info("spec.targetNamespace is deprecated, will be removed in a future release", + "targetNamespace", targetNamespace, + "operatorNamespace", r.OperatorNamespace, ) - targetNamespace = defaultTargetNamespace - } - logger = logger.WithValues("targetNamespace", targetNamespace) - // create if it does not exist - ns := &corev1.Namespace{} - if err := r.Get(ctx, types.NamespacedName{Name: targetNamespace}, ns); err != nil { - if !errors.IsNotFound(err) { - logger.Info("retrieving target namespace %s error: %s", targetNamespace, err.Error()) - return RequeueOnError(err) - } - ns.Name = targetNamespace - - if err = r.Create(ctx, ns, &client.CreateOptions{Raw: &metav1.CreateOptions{}}); err != nil { - if !errors.IsAlreadyExists(err) { - logger.Info("creating target namespace %s error: %s", targetNamespace, err.Error()) + // Deprecated path: create the target namespace if it does not exist + ns := &corev1.Namespace{} + if err := r.Get(ctx, types.NamespacedName{Name: targetNamespace}, ns); err != nil { + if !errors.IsNotFound(err) { + logger.Info("retrieving target namespace %s error: %s", targetNamespace, err.Error()) return RequeueOnError(err) } + ns.Name = targetNamespace + + if err = r.Create(ctx, ns, &client.CreateOptions{Raw: &metav1.CreateOptions{}}); err != nil { + if !errors.IsAlreadyExists(err) { + logger.Info("creating target namespace %s error: %s", targetNamespace, err.Error()) + return RequeueOnError(err) + } + } + logger.Info("created target namespace") } - logger.Info("created target namespace") + } else { + targetNamespace = r.OperatorNamespace } + logger = logger.WithValues("targetNamespace", targetNamespace) // ReconcileCertManager if common.BoolFromEnvVar(UseManagedWebhookCerts) { diff --git a/controllers/shipwrightbuild_controller_test.go b/controllers/shipwrightbuild_controller_test.go index 30460e5d0..afaab17a2 100644 --- a/controllers/shipwrightbuild_controller_test.go +++ b/controllers/shipwrightbuild_controller_test.go @@ -32,6 +32,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +// testOperatorNamespace is the namespace used as the operator's namespace in unit tests. +const testOperatorNamespace = "shipwright-operator" + // bootstrapShipwrightBuildReconciler start up a new instance of ShipwrightBuildReconciler which is // ready to interact with Manifestival, returning the Manifestival instance and the client. func bootstrapShipwrightBuildReconciler( @@ -76,7 +79,7 @@ func bootstrapShipwrightBuildReconciler( } else { toClient = tektonoperatorv1alpha1client.NewSimpleClientset(tcfg) } - r := &ShipwrightBuildReconciler{CRDClient: crdClient.ApiextensionsV1(), TektonOperatorClient: toClient.OperatorV1alpha1(), Client: c, Scheme: s, Logger: logger} + r := &ShipwrightBuildReconciler{CRDClient: crdClient.ApiextensionsV1(), TektonOperatorClient: toClient.OperatorV1alpha1(), Client: c, Scheme: s, Logger: logger, OperatorNamespace: testOperatorNamespace} // creating targetNamespace on which Shipwright-Build will be deployed against, before the other // tests takes place @@ -248,7 +251,7 @@ func TestShipwrightBuildReconciler_Reconcile(t *testing.T) { targetNamespace: "namespace", }, { testName: "target namespace is not informed", - targetNamespace: defaultTargetNamespace, + targetNamespace: testOperatorNamespace, }} for _, tt := range tests { diff --git a/controllers/shipwrightbuild_rbac.go b/controllers/shipwrightbuild_rbac.go index 85b7b2f74..b694672d1 100644 --- a/controllers/shipwrightbuild_rbac.go +++ b/controllers/shipwrightbuild_rbac.go @@ -9,6 +9,7 @@ package controllers // +kubebuilder:rbac:groups=apps,resources=deployments/finalizers,resourceNames=shipwright-build-controller,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,resourceNames=shipwright-build-webhook,verbs=update;patch;delete // +kubebuilder:rbac:groups=apps,resources=deployments/finalizers,resourceNames=shipwright-build-webhook,verbs=update +// TODO(#241): Reduce to get;list;watch once TargetNamespace field is fully removed // +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods;events;configmaps;secrets;limitranges;namespaces;services,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 456cbebc9..ee746640b 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -302,6 +302,7 @@ var _ = BeforeSuite(func() { Client: mgr.GetClient(), Scheme: scheme.Scheme, Logger: ctrl.Log.WithName("controllers").WithName("shipwrightbuild"), + OperatorNamespace: testOperatorNamespace, }).SetupWithManager(mgr) Expect(err).NotTo(HaveOccurred()) diff --git a/docs/shipwrightbuild.md b/docs/shipwrightbuild.md index cb9dc1d10..75c39d05a 100644 --- a/docs/shipwrightbuild.md +++ b/docs/shipwrightbuild.md @@ -29,6 +29,6 @@ When the `ShipwrightBuild` instance is created, the following components are ins | Field | Description | | ----- | ----------- | -| spec.targetNamespace | The target namespace where Shipwright Build will be deployed. If omitted, this will default to `shipwright-build` | +| spec.targetNamespace | **Deprecated.** The target namespace where Shipwright Build will be deployed. If omitted, operands are deployed in the operator's own namespace (determined by the `POD_NAMESPACE` environment variable). Setting this field still works but logs a deprecation warning. This field will be removed in a future release. | | spec.triggers.deployment | When set to `Enabled`, deploys Shipwright Triggers alongside Build. Triggers are not deployed when this field is omitted or set to `Disabled`. Defaults to `Disabled`. | | status.conditions | Conditions which report the status of Shipwright Build. Current reported conditions:

- `Ready` | diff --git a/main.go b/main.go index a9e6d130e..68e377095 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ import ( operatorv1alpha1 "github.com/shipwright-io/operator/api/v1alpha1" "github.com/shipwright-io/operator/controllers" + "github.com/shipwright-io/operator/pkg/common" // +kubebuilder:scaffold:imports ) @@ -99,12 +100,19 @@ func main() { os.Exit(1) } + operatorNamespace := common.OperatorNamespace() + if operatorNamespace == "" { + operatorNamespace = common.DefaultNamespace + setupLog.Info("POD_NAMESPACE not set, falling back to default namespace for local development", "namespace", operatorNamespace) + } + if err = (&controllers.ShipwrightBuildReconciler{ CRDClient: crdClient, TektonOperatorClient: tektonOperatorClient, Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Logger: ctrl.Log.WithName("controllers").WithName("ShipwrightBuild"), + OperatorNamespace: operatorNamespace, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ShipwrightBuild") os.Exit(1) diff --git a/pkg/common/const.go b/pkg/common/const.go index 0bb07f481..2fc1c24bf 100644 --- a/pkg/common/const.go +++ b/pkg/common/const.go @@ -4,6 +4,11 @@ const ( koDataPathEnv = "KO_DATA_PATH" ShipwrightImagePrefix = "IMAGE_SHIPWRIGHT_" + // OperatorNamespaceEnvVar holds the operator pod's namespace. + OperatorNamespaceEnvVar = "POD_NAMESPACE" + // DefaultNamespace is the fallback namespace when POD_NAMESPACE is unset. + DefaultNamespace = "shipwright-build" + TektonOpMinSupportedVersion = "v0.50.0" TektonOpMinSupportedMajor = 0 TektonOpMinSupportedMinor = 50 diff --git a/pkg/common/util.go b/pkg/common/util.go index ad3b9634c..08af4abfb 100644 --- a/pkg/common/util.go +++ b/pkg/common/util.go @@ -242,3 +242,8 @@ func itemInSlice(item string, items []string) bool { func IsOpenShiftPlatform() bool { return os.Getenv("PLATFORM") == "openshift" } + +// OperatorNamespace returns the operator pod's namespace from POD_NAMESPACE. +func OperatorNamespace() string { + return os.Getenv(OperatorNamespaceEnvVar) +}