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
This commit is contained in:
IO ANALYTICA 2026-07-28 16:02:59 +02:00 committed by GitHub
parent 8eda2cd1a2
commit 29168ca0bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 17 additions and 126 deletions

View File

@ -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 "<namespace>/<name>". 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:

View File

@ -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 "<namespace>/<name>". 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:

View File

@ -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 "<namespace>/<name>". 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:

View File

@ -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
// ("<namespace>/<name>") 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"`