Skip to content

MCO-2142: OSImageStream.spec.defaultStream validation#2736

Open
pablintino wants to merge 1 commit intoopenshift:masterfrom
pablintino:mco-2142
Open

MCO-2142: OSImageStream.spec.defaultStream validation#2736
pablintino wants to merge 1 commit intoopenshift:masterfrom
pablintino:mco-2142

Conversation

@pablintino
Copy link
Contributor

Add a validation to the OSImageStream resource to ensure that the .spec.defaultStream fiel, when set, points to a value given in the .status.availableStreams.
If the resourece has no status yet the validation should just pass and the MCO operator should take care of handling such spec. This only happens at cluster bootstrapping.

@openshift-ci-robot
Copy link

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 3, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Mar 3, 2026

@pablintino: This pull request references MCO-2142 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Add a validation to the OSImageStream resource to ensure that the .spec.defaultStream fiel, when set, points to a value given in the .status.availableStreams.
If the resourece has no status yet the validation should just pass and the MCO operator should take care of handling such spec. This only happens at cluster bootstrapping.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 3, 2026

Hello @pablintino! Some important instructions when contributing to openshift/api:
API design plays an important part in the user experience of OpenShift and as such API PRs are subject to a high level of scrutiny to ensure they follow our best practices. If you haven't already done so, please review the OpenShift API Conventions and ensure that your proposed changes are compliant. Following these conventions will help expedite the api review process for your PR.

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20a7119f-c311-4d8a-b295-3d096655db6d

📥 Commits

Reviewing files that changed from the base of the PR and between 03a376c and b8917f1.

⛔ Files ignored due to path filters (3)
  • machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • machineconfiguration/v1alpha1/zz_generated.featuregated-crd-manifests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • openapi/generated_openapi/zz_generated.openapi.go is excluded by !openapi/**
📒 Files selected for processing (4)
  • machineconfiguration/v1alpha1/tests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml
  • machineconfiguration/v1alpha1/types_osimagestream.go
  • machineconfiguration/v1alpha1/zz_generated.swagger_doc_generated.go
  • payload-manifests/crds/0000_80_machine-config_01_osimagestreams.crd.yaml
✅ Files skipped from review due to trivial changes (1)
  • machineconfiguration/v1alpha1/zz_generated.swagger_doc_generated.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • payload-manifests/crds/0000_80_machine-config_01_osimagestreams.crd.yaml

📝 Walkthrough

Walkthrough

Adds cross-field validation for OSImageStream so spec.defaultStream must reference a name present in status.availableStreams when status is populated. Implements Kubebuilder XValidation annotations in the Go type and the CRD x-kubernetes-validations rule, updates Swagger docs for defaultStream, and expands tests with numerous onUpdate cases covering positive and negative scenarios: cross-field consistency, stream-name RFC 1123 and length checks, image URL/digest formatting, and required-field validations.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding validation for OSImageStream.spec.defaultStream. It references the JIRA issue (MCO-2142) and is concise.
Description check ✅ Passed The description accurately explains the validation being added and the handling of resources without status, which aligns with the changeset across all four files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented
The command is terminated due to an error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented


Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Mar 3, 2026
@qodo-code-review
Copy link

Review Summary by Qodo

Add OSImageStream.spec.defaultStream validation rule
✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add CEL validation rule to OSImageStream.spec.defaultStream field
  - Ensures spec.defaultStream references existing stream in status.availableStreams
  - Allows validation to pass when status is not yet populated (bootstrap scenario)
• Add comprehensive test cases covering validation scenarios
  - Valid stream updates, invalid stream references, and stream removal cases
• Update generated CRD manifests with new validation rule
Diagram
flowchart LR
  A["OSImageStream Type Definition"] -->|adds CEL validation| B["spec.defaultStream Validation"]
  B -->|checks if exists in| C["status.availableStreams"]
  B -->|allows missing| D["Bootstrap Scenario"]
  E["Test Cases"] -->|validates| B
  F["CRD Manifests"] -->|generated from| A
Loading

Grey Divider

File Changes

1. machineconfiguration/v1alpha1/types_osimagestream.go ✨ Enhancement +1/-0

Add CEL validation for defaultStream field

• Added kubebuilder CEL validation rule to OSImageStream type
• Rule validates spec.defaultStream references existing stream name from status.availableStreams
• Validation gracefully handles missing spec, defaultStream, or status fields

machineconfiguration/v1alpha1/types_osimagestream.go


2. machineconfiguration/v1alpha1/tests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml 🧪 Tests +117/-0

Add validation test cases for defaultStream

• Added test case for valid spec.defaultStream update to existing stream
• Added test case for rejecting invalid spec.defaultStream reference
• Added test case for rejecting removal of referenced stream from status.availableStreams

machineconfiguration/v1alpha1/tests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml


3. machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml ⚙️ Configuration changes +4/-0

Update generated CRD with validation rule

• Updated generated CRD manifest with new x-kubernetes-validations rule
• Added validation message and CEL rule for spec.defaultStream field

machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml


View more (2)
4. machineconfiguration/v1alpha1/zz_generated.featuregated-crd-manifests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml ⚙️ Configuration changes +4/-0

Update generated feature-gated CRD manifest

• Updated generated feature-gated CRD manifest with new x-kubernetes-validations rule
• Added validation message and CEL rule for spec.defaultStream field

machineconfiguration/v1alpha1/zz_generated.featuregated-crd-manifests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml


5. payload-manifests/crds/0000_80_machine-config_01_osimagestreams.crd.yaml ⚙️ Configuration changes +4/-0

Update payload CRD manifest

• Updated payload CRD manifest with new x-kubernetes-validations rule
• Added validation message and CEL rule for spec.defaultStream field

payload-manifests/crds/0000_80_machine-config_01_osimagestreams.crd.yaml


Grey Divider

Qodo Logo

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 3, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. spec.defaultStream rule undocumented 📘 Rule violation ✓ Correctness
Description
A new XValidation enforces that spec.defaultStream must match a name in status.availableStreams
when status is present, but the spec.defaultStream field comment does not document this
constraint (or the bootstrapping exception when status is absent). This can surprise API consumers
because updates may be rejected by validation without the behavior being described in-field
documentation.
Code

machineconfiguration/v1alpha1/types_osimagestream.go[26]

+// +kubebuilder:validation:XValidation:rule="!has(self.spec) || !has(self.spec.defaultStream) || !has(self.status) || self.spec.defaultStream in self.status.availableStreams.map(s, s.name)",message="spec.defaultStream must reference an existing stream name from status.availableStreams"
Evidence
PR Compliance ID 5 requires field comments to document all validation constraints, including those
enforced via XValidation. The PR adds an XValidation that constrains spec.defaultStream based on
status.availableStreams, but the OSImageStreamSpec.DefaultStream comment only documents RFC1123
formatting and does not state the new membership constraint or the special case when status is not
present.

AGENTS.md
machineconfiguration/v1alpha1/types_osimagestream.go[26-26]
machineconfiguration/v1alpha1/types_osimagestream.go[78-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new type-level XValidation enforces that `spec.defaultStream` must be one of `status.availableStreams[].name` when `status` exists, but the `spec.defaultStream` field comment does not document this constraint (nor the bootstrapping exception when `status` is not present).

## Issue Context
Compliance requires that field comments fully describe all validation/optionality constraints that apply to the field, including constraints enforced via XValidation.

## Fix Focus Areas
- machineconfiguration/v1alpha1/types_osimagestream.go[26-26]
- machineconfiguration/v1alpha1/types_osimagestream.go[76-96]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Missing fieldPath for error 🐞 Bug ✧ Quality
Description
The new cross-field XValidation is attached at the object level without a fieldPath, so validation
failures will be reported against the whole object rather than pinpointing .spec.defaultStream. This
reduces error usability/automation compared to other CRDs in this repo that scope cross-field
validations to a specific field via fieldPath.
Code

machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml[R187-190]

+        - message: spec.defaultStream must reference an existing stream name from
+            status.availableStreams
+          rule: '!has(self.spec) || !has(self.spec.defaultStream) || !has(self.status)
+            || self.spec.defaultStream in self.status.availableStreams.map(s, s.name)'
Evidence
The OSImageStream CRD’s newly-added x-kubernetes-validations entry provides only message/rule (no
fieldPath). Elsewhere in the repo, similar cross-field validations use fieldPath to associate the
error with a specific field, improving debuggability and tooling integration.

machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml[184-190]
machineconfiguration/v1alpha1/types_osimagestream.go[23-27]
example/v1/zz_generated.featuregated-crd-manifests/stableconfigtypes.example.openshift.io/Example.yaml[359-364]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The OSImageStream top-level XValidation for `spec.defaultStream` does not specify a `fieldPath`, so API validation errors are reported at the object level rather than being attributed to `.spec.defaultStream`.

### Issue Context
Other CRDs in this repo use `fieldPath` for cross-field validations to improve error clarity and toolability.

### Fix Focus Areas
- machineconfiguration/v1alpha1/types_osimagestream.go[23-27]
- machineconfiguration/v1alpha1/zz_generated.crd-manifests/0000_80_machine-config_01_osimagestreams.crd.yaml[184-190]
- machineconfiguration/v1alpha1/zz_generated.featuregated-crd-manifests/osimagestreams.machineconfiguration.openshift.io/OSStreams.yaml[184-190]
- payload-manifests/crds/0000_80_machine-config_01_osimagestreams.crd.yaml[184-190]

### Suggested change
1. Update the kubebuilder marker to include `fieldPath` (e.g. `fieldPath=".spec.defaultStream"` if supported by the marker format).
2. Regenerate CRD manifests so the generated YAML includes:
  - `fieldPath: .spec.defaultStream`
  on the new validation rule.
3. Ensure existing tests still match (they match substrings, so this should be non-breaking).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@openshift-ci openshift-ci bot requested review from JoelSpeed and yuqi-zhang March 3, 2026 10:56
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 3, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign joelspeed for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

// +openshift:enable:FeatureGate=OSStreams
// +kubebuilder:metadata:labels=openshift.io/operator-managed=
// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="osimagestream is a singleton, .metadata.name must be 'cluster'"
// +kubebuilder:validation:XValidation:rule="!has(self.spec) || !has(self.spec.defaultStream) || !has(self.status) || self.spec.defaultStream in self.status.availableStreams.map(s, s.name)",message="spec.defaultStream must reference an existing stream name from status.availableStreams"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers: The .status.availableStreams is only refreshed during an update (and at install time of course). It may happen that the stream the user has selected is no longer available. The idea we have is to signal the user in a previous release that a stream is EoL and force them to remove all usages of that stream before allowing them to update with upgradable=False. That way, the source version is warrantied to point to streams available in the target version of the update.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this a bit as a team. Essentially this saying that this validation would be different payload-to-payload (so breaking changes can theoretically occur as the validation "tightens", but in practice we'll always make sure that the user is on a default stream that is available on the payload it would upgrade to (and we'd never remove a stream in a z-stream, only y-stream). Therefore this would only ever check for user-action changes to this field.

With that in mind, we also debated whether this would be better just as a controller level validation instead, since it is a bit of an odd case of availableStreams that change from version to version.

(also as a note, I think !has(self.spec) is redundant since spec is a required field.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to remove the not needed spec check.

// +openshift:enable:FeatureGate=OSStreams
// +kubebuilder:metadata:labels=openshift.io/operator-managed=
// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="osimagestream is a singleton, .metadata.name must be 'cluster'"
// +kubebuilder:validation:XValidation:rule="!has(self.spec) || !has(self.spec.defaultStream) || !has(self.status) || self.spec.defaultStream in self.status.availableStreams.map(s, s.name)",message="spec.defaultStream must reference an existing stream name from status.availableStreams"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. spec.defaultstream rule undocumented 📘 Rule violation ✓ Correctness

A new XValidation enforces that spec.defaultStream must match a name in status.availableStreams
when status is present, but the spec.defaultStream field comment does not document this
constraint (or the bootstrapping exception when status is absent). This can surprise API consumers
because updates may be rejected by validation without the behavior being described in-field
documentation.
Agent Prompt
## Issue description
A new type-level XValidation enforces that `spec.defaultStream` must be one of `status.availableStreams[].name` when `status` exists, but the `spec.defaultStream` field comment does not document this constraint (nor the bootstrapping exception when `status` is not present).

## Issue Context
Compliance requires that field comments fully describe all validation/optionality constraints that apply to the field, including constraints enforced via XValidation.

## Fix Focus Areas
- machineconfiguration/v1alpha1/types_osimagestream.go[26-26]
- machineconfiguration/v1alpha1/types_osimagestream.go[76-96]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 3, 2026

@pablintino: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/images 03a376c link true /test images

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Add a validation to the OSImageStream resource to ensure that the
.spec.defaultStream fiel, when set, points to a value given in the
.status.availableStreams.
If the resourece has no status yet the validation should just pass and
the MCO operator should take care of handling such spec. This only
happens at cluster bootstrapping.

Signed-off-by: Pablo Rodriguez Nava <git@amail.pablintino.com>
// +openshift:enable:FeatureGate=OSStreams
// +kubebuilder:metadata:labels=openshift.io/operator-managed=
// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="osimagestream is a singleton, .metadata.name must be 'cluster'"
// +kubebuilder:validation:XValidation:rule="!has(self.spec.defaultStream) || !has(self.status) || self.spec.defaultStream in self.status.availableStreams.map(s, s.name)",message="spec.defaultStream must reference an existing stream name from status.availableStreams"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to test this, integration ratcheting tests in particular.

You'll also want to make sure that this only rejects spec writes.

Imagine a case where the user forces through any upgrade checks, currently a status update could fail if their spec value is no longer present in the spec list after update.

Adjust this to accept any time self.spec == oldSelf.spec would be a useful guard here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants