From 29168ca0bc91d15f4fc0cc92cad5b8afcc593fb9 Mon Sep 17 00:00:00 2001 From: IO ANALYTICA <83664220+ioanalytica@users.noreply.github.com> Date: Tue, 28 Jul 2026 16:02:59 +0200 Subject: [PATCH] Fix operatorconfigurations CRD: NamespacedName must render as type string (#3145) Upgrading to v2.0.0 fails at the CRD apply step. The apiserver rejects the OperatorConfiguration CRD: CustomResourceDefinition "operatorconfigurations.acid.zalan.do" is invalid: ...oauth_token_secret_name.default: Invalid value: "string": in body must be of type object Root cause: these CRDs are generated by controller-gen from the Go types. spec.NamespacedName is a struct {Namespace, Name}, so controller-gen emits an object schema for every NamespacedName field. But NamespacedName has custom MarshalJSON/UnmarshalJSON that (de)serialize it as a plain JSON string ("namespace/name"). The generated object schema therefore never matched how the operator actually reads and writes these fields (it did in 1.15.x, where they were type: string). For oauth_token_secret_name this is a hard failure: it also carries `// +kubebuilder:default=postgres-operator`, and a scalar string default on an object-typed property is rejected by the apiserver, blocking the whole 1.15.x -> 2.0.0 upgrade before the operator Deployment is touched. Fix at the source by annotating the NamespacedName type with `// +kubebuilder:validation:Type=string` and regenerating the CRDs. This makes controller-gen emit `type: string` for all NamespacedName fields (oauth_token_secret_name, infrastructure_roles_secret_name, pod_environment_configmap, and the nested infrastructure role secretname), matching their runtime serialization and restoring 1.15.x behaviour. A field-level Type override cannot be used here: it conflicts with the struct-derived schema ("object vs string in allOf") and fails generation. Regenerated manifests/operatorconfiguration.crd.yaml and its two synced copies (pkg/apis/... embed source and charts/.../crds). Verified with a server-side dry-run apply: the unpatched CRD is rejected, the regenerated CRD is accepted. Fixes #3143 --- .../crds/operatorconfigurations.yaml | 46 ++----------------- manifests/operatorconfiguration.crd.yaml | 46 ++----------------- .../v1/operatorconfiguration.crd.yaml | 46 ++----------------- pkg/spec/types.go | 5 ++ 4 files changed, 17 insertions(+), 126 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 68a934cf9..5084b72aa 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -226,24 +226,7 @@ spec: type: string type: array infrastructure_roles_secret_name: - description: |- - NamespacedName comprises a resource name, with a mandatory namespace, - rendered as "/". Being a type captures intent and - helps make sure that UIDs, namespaced names and non-namespaced names - do not get conflated in code. For most use cases, namespace and name - will already have been format validated at the API entry point, so we - don't do that here. Where that's not the case (e.g. in testing), - consider using NamespacedNameOrDie() in testing.go in this package. - - from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string infrastructure_roles_secrets: description: namespaced name of the secret containing infrastructure roles names and passwords @@ -265,14 +248,7 @@ spec: description: |- Name of a secret which describes the role, and optionally name of a configmap with an extra information - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string template: type: boolean userkey: @@ -458,14 +434,7 @@ spec: default: postgres-operator description: namespaced name of the secret containing the OAuth2 token to pass to the teams API - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pdb_master_label_selector: default: true type: boolean @@ -485,14 +454,7 @@ spec: pod_environment_configmap: description: namespaced name of the ConfigMap with environment variables to populate on every pod - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pod_environment_secret: type: string pod_management_policy: diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 68a934cf9..5084b72aa 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -226,24 +226,7 @@ spec: type: string type: array infrastructure_roles_secret_name: - description: |- - NamespacedName comprises a resource name, with a mandatory namespace, - rendered as "/". Being a type captures intent and - helps make sure that UIDs, namespaced names and non-namespaced names - do not get conflated in code. For most use cases, namespace and name - will already have been format validated at the API entry point, so we - don't do that here. Where that's not the case (e.g. in testing), - consider using NamespacedNameOrDie() in testing.go in this package. - - from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string infrastructure_roles_secrets: description: namespaced name of the secret containing infrastructure roles names and passwords @@ -265,14 +248,7 @@ spec: description: |- Name of a secret which describes the role, and optionally name of a configmap with an extra information - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string template: type: boolean userkey: @@ -458,14 +434,7 @@ spec: default: postgres-operator description: namespaced name of the secret containing the OAuth2 token to pass to the teams API - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pdb_master_label_selector: default: true type: boolean @@ -485,14 +454,7 @@ spec: pod_environment_configmap: description: namespaced name of the ConfigMap with environment variables to populate on every pod - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pod_environment_secret: type: string pod_management_policy: diff --git a/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml index 68a934cf9..5084b72aa 100644 --- a/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml +++ b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml @@ -226,24 +226,7 @@ spec: type: string type: array infrastructure_roles_secret_name: - description: |- - NamespacedName comprises a resource name, with a mandatory namespace, - rendered as "/". Being a type captures intent and - helps make sure that UIDs, namespaced names and non-namespaced names - do not get conflated in code. For most use cases, namespace and name - will already have been format validated at the API entry point, so we - don't do that here. Where that's not the case (e.g. in testing), - consider using NamespacedNameOrDie() in testing.go in this package. - - from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string infrastructure_roles_secrets: description: namespaced name of the secret containing infrastructure roles names and passwords @@ -265,14 +248,7 @@ spec: description: |- Name of a secret which describes the role, and optionally name of a configmap with an extra information - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string template: type: boolean userkey: @@ -458,14 +434,7 @@ spec: default: postgres-operator description: namespaced name of the secret containing the OAuth2 token to pass to the teams API - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pdb_master_label_selector: default: true type: boolean @@ -485,14 +454,7 @@ spec: pod_environment_configmap: description: namespaced name of the ConfigMap with environment variables to populate on every pod - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + type: string pod_environment_secret: type: string pod_management_policy: diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 3f4abb70f..642f3fd9b 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -23,6 +23,11 @@ import ( // consider using NamespacedNameOrDie() in testing.go in this package. // // from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go +// +// NamespacedName marshals to and unmarshals from a plain JSON string +// ("/") via its custom MarshalJSON/UnmarshalJSON, so its CRD +// schema must be a string rather than the struct controller-gen would infer. +// +kubebuilder:validation:Type=string type NamespacedName struct { Namespace string `json:"namespace,omitempty"` Name string `json:"name"`