Skip to content

Commit d3ee043

Browse files
committed
address PR review feedback
1 parent d30a2bc commit d3ee043

28 files changed

Lines changed: 379 additions & 440 deletions

api/v1/clusterextension_types.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,34 +49,43 @@ const (
4949

5050
// ClusterExtensionSpec defines the desired state of ClusterExtension
5151
type ClusterExtensionSpec struct {
52-
// namespace selects the namespace that namespace-scoped resources for the extension
53-
// are applied to.
54-
//
5552
// <opcon:standard:description>
56-
// In the standard configuration, namespace is required and must reference an existing
57-
// namespace on the cluster.
53+
// namespace specifies a Kubernetes namespace.
54+
// It designates the default namespace where namespace-scoped resources for the extension are applied to the cluster.
55+
// Some extensions may contain namespace-scoped resources to be applied in other namespaces.
56+
// This namespace must exist.
57+
//
58+
// The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123].
59+
// It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,
60+
// and be no longer than 63 characters.
61+
//
62+
// [RFC 1123]: https://tools.ietf.org/html/rfc1123
5863
// </opcon:standard:description>
5964
// <opcon:experimental:description>
60-
// BoxcutterRuntime feature set, namespace is optional.
61-
// When set, it must reference an existing namespace. When omitted, operator-controller
62-
// resolves and creates a managed namespace from bundle metadata. The mode (set vs omitted)
63-
// is locked at creation time and cannot be changed.
64-
// </opcon:experimental:description>
65+
// namespace selects the namespace that namespace-scoped resources for the extension
66+
// are applied to.
67+
//
68+
// namespace is optional. When set, it must reference an existing namespace on the cluster.
69+
// When omitted, operator-controller resolves and creates a managed namespace from the
70+
// bundle's metadata. Whether namespace is set or omitted is fixed at creation time and
71+
// cannot be changed afterwards.
6572
//
6673
// The namespace field follows the DNS label standard as defined in [RFC 1123].
6774
// It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,
6875
// and be no longer than 63 characters.
6976
//
7077
// [RFC 1123]: https://tools.ietf.org/html/rfc1123
78+
// </opcon:experimental:description>
7179
//
72-
// <opcon:standard:validation:Required>
73-
// <opcon:standard:validation:XValidation:rule="self != ''",message="namespace is required">
80+
// <opcon:standard:validation:XValidation:rule="self == oldSelf",message="namespace is immutable">
81+
// <opcon:standard:validation:XValidation:rule="self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")",message="namespace must be a valid DNS1123 label">
82+
// <opcon:experimental:validation:XValidation:rule="self == '' || self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")",message="namespace must be a valid DNS1123 label">
83+
// <opcon:experimental:validation:XValidation:rule="oldSelf == '' || self == oldSelf",message="namespace is immutable once set">
7484
// <opcon:experimental:validation:XValidation:rule="oldSelf != '' || self == ''",message="namespace cannot be set after creation; mode is locked at creation time">
85+
// <opcon:experimental:validation:Optional>
7586
//
7687
// +kubebuilder:validation:MaxLength:=63
77-
// +kubebuilder:validation:XValidation:rule="self == '' || self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label"
78-
// +kubebuilder:validation:XValidation:rule="oldSelf == '' || self == oldSelf",message="namespace is immutable once set"
79-
// +optional
88+
// +required
8089
Namespace string `json:"namespace"`
8190

8291
// serviceAccount is a deprecated field and is completely ignored.
@@ -599,6 +608,8 @@ type ClusterExtension struct {
599608
metav1.ObjectMeta `json:"metadata,omitempty"`
600609

601610
// spec is an optional field that defines the desired state of the ClusterExtension.
611+
//
612+
// <opcon:experimental:validation:XValidation:rule="has(oldSelf.namespace) == has(self.namespace)",message="namespace presence is immutable; it cannot be added or removed after creation">
602613
// +optional
603614
Spec ClusterExtensionSpec `json:"spec,omitempty"`
604615

applyconfigurations/api/v1/clusterextension.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfigurations/api/v1/clusterextensionspec.go

Lines changed: 25 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/operator-controller/main.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -502,12 +502,12 @@ func run() error {
502502

503503
certProvider := getCertificateProvider()
504504
regv1ManifestProvider := &applier.RegistryV1ManifestProvider{
505-
BundleRenderer: registryv1.Renderer,
506-
CertificateProvider: certProvider,
507-
IsWebhookSupportEnabled: certProvider != nil,
508-
IsSingleOwnNamespaceEnabled: features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport),
509-
IsDeploymentConfigEnabled: features.OperatorControllerFeatureGate.Enabled(features.DeploymentConfig),
510-
IsBoxcutterRuntimeEnabled: features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime),
505+
BundleRenderer: registryv1.Renderer,
506+
CertificateProvider: certProvider,
507+
IsWebhookSupportEnabled: certProvider != nil,
508+
IsSingleOwnNamespaceEnabled: features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport),
509+
IsDeploymentConfigEnabled: features.OperatorControllerFeatureGate.Enabled(features.DeploymentConfig),
510+
IsNamespaceManagementEnabled: features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime),
511511
}
512512
var cerCfg reconcilerConfigurator
513513
if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) {
@@ -748,7 +748,6 @@ func (c *helmReconcilerConfigurator) Configure(ceReconciler *controllers.Cluster
748748
controllers.RetrieveRevisionStates(revisionStatesGetter),
749749
controllers.ResolveBundle(c.resolver, c.mgr.GetClient()),
750750
controllers.UnpackBundle(c.imagePuller, c.imageCache),
751-
controllers.ValidateInstallNamespace(coreClient),
752751
controllers.ApplyBundle(appl),
753752
}
754753

docs/api-reference/olmv1-api-reference.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ _Appears in:_
255255
| `kind` _string_ | Kind is a string value representing the REST resource this object represents.<br />Servers may infer this from the endpoint the client submits requests to.<br />Cannot be updated.<br />In CamelCase.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\} <br /> |
256256
| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.<br />Servers should convert recognized schemas to the latest internal value, and<br />may reject unrecognized values.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\} <br /> |
257257
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\} <br /> |
258-
| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | spec is an optional field that defines the desired state of the ClusterExtension. | | Optional: \{\} <br /> |
258+
| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | spec is an optional field that defines the desired state of the ClusterExtension.<br /> | | Optional: \{\} <br /> |
259259
| `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | status is an optional field that defines the observed state of the ClusterExtension. | | Optional: \{\} <br /> |
260260

261261

@@ -358,7 +358,7 @@ _Appears in:_
358358

359359
| Field | Description | Default | Validation |
360360
| --- | --- | --- | --- |
361-
| `namespace` _string_ | namespace selects the namespace that namespace-scoped resources for the extension<br />are applied to.<br /><br />In the standard configuration, namespace is required and must reference an existing<br />namespace on the cluster.<br /><br /><br />In the experimental configuration (BoxcutterRuntime feature set), namespace is optional.<br />When set, it must reference an existing namespace. When omitted, operator-controller<br />resolves and creates a managed namespace from bundle metadata. The mode (set vs omitted)<br />is locked at creation time and cannot be changed.<br /><br />The namespace field follows the DNS label standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,<br />and be no longer than 63 characters.<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123<br /><br /><br /> | | MaxLength: 63 <br />Optional: \{\} <br /> |
361+
| `namespace` _string_ | <br />namespace specifies a Kubernetes namespace.<br />It designates the default namespace where namespace-scoped resources for the extension are applied to the cluster.<br />Some extensions may contain namespace-scoped resources to be applied in other namespaces.<br />This namespace must exist.<br />The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,<br />and be no longer than 63 characters.<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123<br /><br /><br />namespace selects the namespace that namespace-scoped resources for the extension<br />are applied to.<br />namespace is optional. When set, it must reference an existing namespace on the cluster.<br />When omitted, operator-controller resolves and creates a managed namespace from the<br />bundle's metadata. Whether namespace is set or omitted is fixed at creation time and<br />cannot be changed afterwards.<br />The namespace field follows the DNS label standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,<br />and be no longer than 63 characters.<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123<br /><br /><br /><br /><br /><br /><br /> | | MaxLength: 63 <br />Required: \{\} <br /> |
362362
| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a deprecated field and is completely ignored.<br />OLMv1 is a single-tenant system where users with ClusterExtension write access are<br />effectively delegated cluster-admin trust. The operator-controller runs with<br />cluster-admin privileges and uses its own service account for all cluster interactions.<br />Deprecated: serviceAccount is no longer used and will be removed in a future release. | | MinProperties: 1 <br />Optional: \{\} <br /> |
363363
| `source` _[SourceConfig](#sourceconfig)_ | source is required and selects the installation source of content for this ClusterExtension.<br />Set the sourceType field to perform the selection.<br />Catalog is currently the only implemented sourceType.<br />Setting sourceType to "Catalog" requires the catalog field to also be defined.<br />Below is a minimal example of a source definition (in yaml):<br />source:<br /> sourceType: Catalog<br /> catalog:<br /> packageName: example-package | | Required: \{\} <br /> |
364364
| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is optional and configures installation options for the ClusterExtension,<br />such as the pre-flight check configuration. | | Optional: \{\} <br /> |
File renamed without changes.

hack/tools/crd-generator/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ func opconTweaks(channel string, name string, jsonProps apiextensionsv1.JSONSche
251251
}
252252
}
253253

254-
celRe := regexp.MustCompile(validationPrefix + "XValidation:rule=\"([^\"]*)\",message=\"([^\"]*)\">")
254+
// The rule is captured non-greedily so it may itself contain double quotes (e.g. a CEL
255+
// self.matches("...") call); it extends to the real ",message=" delimiter. The message
256+
// remains quote-free.
257+
celRe := regexp.MustCompile(validationPrefix + "XValidation:rule=\"(.*?)\",message=\"([^\"]*)\">")
255258
celMatches := celRe.FindAllStringSubmatch(jsonProps.Description, 64)
256259
for _, celMatch := range celMatches {
257260
if len(celMatch) != 3 {

hack/tools/crd-generator/main_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ func TestOpconTweaksXValidation(t *testing.T) {
3838
description: `Field description.` + "\n" + `<opcon:experimental:validation:XValidation:rule="oldSelf != '' || self == ''",message="mode is locked at creation time">`,
3939
expectRuleCount: 0,
4040
},
41+
{
42+
name: "rule containing double quotes is captured in full",
43+
channel: StandardChannel,
44+
description: `Field description.` + "\n" + `<opcon:standard:validation:XValidation:rule="self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")",message="namespace must be a valid DNS1123 label">`,
45+
expectRule: `self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")`,
46+
expectMessage: "namespace must be a valid DNS1123 label",
47+
expectRuleCount: 1,
48+
},
49+
{
50+
name: "quoted rule combined with disjunction is captured in full",
51+
channel: ExperimentalChannel,
52+
description: `Field description.` + "\n" + `<opcon:experimental:validation:XValidation:rule="self == '' || self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")",message="namespace must be a valid DNS1123 label">`,
53+
expectRule: `self == '' || self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")`,
54+
expectMessage: "namespace must be a valid DNS1123 label",
55+
expectRuleCount: 1,
56+
},
4157
}
4258

4359
for _, tt := range tests {

helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ spec:
150150
namespace selects the namespace that namespace-scoped resources for the extension
151151
are applied to.
152152
153-
BoxcutterRuntime feature set, namespace is optional.
154-
When set, it must reference an existing namespace. When omitted, operator-controller
155-
resolves and creates a managed namespace from bundle metadata. The mode (set vs omitted)
156-
is locked at creation time and cannot be changed.
153+
namespace is optional. When set, it must reference an existing namespace on the cluster.
154+
When omitted, operator-controller resolves and creates a managed namespace from the
155+
bundle's metadata. Whether namespace is set or omitted is fixed at creation time and
156+
cannot be changed afterwards.
157157
158158
The namespace field follows the DNS label standard as defined in [RFC 1123].
159159
It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character,
@@ -501,6 +501,10 @@ spec:
501501
required:
502502
- source
503503
type: object
504+
x-kubernetes-validations:
505+
- message: namespace presence is immutable; it cannot be added or removed
506+
after creation
507+
rule: has(oldSelf.namespace) == has(self.namespace)
504508
status:
505509
description: status is an optional field that defines the observed state
506510
of the ClusterExtension.

0 commit comments

Comments
 (0)