Skip to content

Conversation

@QiWang19
Copy link
Member

@QiWang19 QiWang19 commented Nov 1, 2025

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 1, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 1, 2025
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 1, 2025

Hello @QiWang19! 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.

@openshift-ci openshift-ci bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Nov 1, 2025
@QiWang19 QiWang19 changed the title Add CRIOCredentialProviderConfig API and related functionality Add CRIOCredentialProviderConfig API Nov 1, 2025
@QiWang19
Copy link
Member Author

QiWang19 commented Nov 1, 2025

/retest-required

@QiWang19 QiWang19 force-pushed the creds-provider branch 2 times, most recently from 8805169 to 108219f Compare November 3, 2025 22:49
@QiWang19
Copy link
Member Author

QiWang19 commented Nov 3, 2025

/test integration

@QiWang19 QiWang19 marked this pull request as ready for review November 3, 2025 23:26
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 3, 2025
@QiWang19
Copy link
Member Author

QiWang19 commented Nov 3, 2025

PR has been reviewed by claude code /api-review. It helped run make lint and correct the failures.


// +kubebuilder:validation:MaxLength=512
// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')`,message="invalid matchImages value, must be a valid fully qualified domain name with optional wildcard, port, and path"
type MatchImage string
Copy link
Member Author

Choose a reason for hiding this comment

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

@saschagrunert PTAL. as mentioned in the previsou enhancement discussion(openshift/enhancements#1861 (comment)), this is a stricter rule than the upstream matchImages, as it does not allow wildcard matching of partial subdomains like app*.k8s.io. Customers may raise concerns about this difference, but it simplifies the configuration.
What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

I mean, we can always make it looser later on while starting with a stricter regex. It's already fairly complex and we need extensive testing on that validation part.

The docs needs to be updated for this type, like:

// MatchImage is a string pattern used to match container image registry addresses.
// It must be a valid fully qualified domain name with optional wildcard, port, and path.
// The maximum length is 512 characters.
//
// Wildcards ('*') are supported for full subdomain labels and top-level domains.
// Each entry can optionally contain a port (e.g., :8080) and a path (e.g., /path).
// Wildcards are not allowed in the port or path portions.
//
// Examples:
// - "registry.io" - matches exactly registry.io
// - "*.azurecr.io" - matches any single subdomain of azurecr.io
// - "registry.io:8080/path" - matches with specific port and path prefix
//
// +kubebuilder:validation:MaxLength=512
// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')`,message="invalid matchImages value, must be a valid fully qualified domain name with optional wildcard, port, and path"
type MatchImage string


// +kubebuilder:validation:MaxLength=512
// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')`,message="invalid matchImages value, must be a valid fully qualified domain name with optional wildcard, port, and path"
type MatchImage string
Copy link
Member

Choose a reason for hiding this comment

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

I mean, we can always make it looser later on while starting with a stricter regex. It's already fairly complex and we need extensive testing on that validation part.

The docs needs to be updated for this type, like:

// MatchImage is a string pattern used to match container image registry addresses.
// It must be a valid fully qualified domain name with optional wildcard, port, and path.
// The maximum length is 512 characters.
//
// Wildcards ('*') are supported for full subdomain labels and top-level domains.
// Each entry can optionally contain a port (e.g., :8080) and a path (e.g., /path).
// Wildcards are not allowed in the port or path portions.
//
// Examples:
// - "registry.io" - matches exactly registry.io
// - "*.azurecr.io" - matches any single subdomain of azurecr.io
// - "registry.io:8080/path" - matches with specific port and path prefix
//
// +kubebuilder:validation:MaxLength=512
// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')`,message="invalid matchImages value, must be a valid fully qualified domain name with optional wildcard, port, and path"
type MatchImage string

Comment on lines 33 to 38
// status represents the current state of the CRIOCredentialProviderConfig.
// +optional
Status *CRIOCredentialProviderConfigStatus `json:"status,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

Optional fields must explain what happens when they are omitted.

Suggested change
// status represents the current state of the CRIOCredentialProviderConfig.
// +optional
Status *CRIOCredentialProviderConfigStatus `json:"status,omitempty"`
// status represents the current state of the CRIOCredentialProviderConfig.
// When omitted or nil, it indicates that the status has not yet been set by the controller.
// The controller will populate this field with validation conditions and operational state.
// +optional
Status *CRIOCredentialProviderConfigStatus `json:"status,omitempty"`

Comment on lines 84 to 89
// conditions represent the latest available observations of the configuration state
// +optional
// +kubebuilder:validation:MaxItems=4
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

This field has three validation markers (MaxItems, listType, listMapKey) that must all be documented. The comment must explain:

  • behavior when omitted
  • the maximum of 4 items
  • that it's a map type keyed by type
  • what condition types are expected based on the constants defined in the file
Suggested change
// conditions represent the latest available observations of the configuration state
// +optional
// +kubebuilder:validation:MaxItems=4
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
// conditions represent the latest available observations of the configuration state.
// When omitted or empty, it indicates that no conditions have been reported yet.
// The maximum number of conditions is 4.
// Conditions are stored as a map keyed by condition type, ensuring uniqueness.
//
// Expected condition types include:
// - "Validated": indicates whether the matchImages configuration is valid
// +optional
// +kubebuilder:validation:MaxItems=4
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`

Comment on lines 29 to 32
// spec defines the desired configuration of the CRIO Credential Provider.
// +required
Spec CRIOCredentialProviderConfigSpec `json:"spec,omitzero"`
Copy link
Member

Choose a reason for hiding this comment

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

Per API conventions, required fields should explicitly state in their documentation that they are required.

Suggested change
// spec defines the desired configuration of the CRIO Credential Provider.
// +required
Spec CRIOCredentialProviderConfigSpec `json:"spec,omitzero"`
// spec defines the desired configuration of the CRIO Credential Provider.
// This field is required and must be provided when creating the resource.
// +required
Spec CRIOCredentialProviderConfigSpec `json:"spec,omitzero"`

// passed to the kubelet CredentialProviderConfig, and if any pattern matches
// the requested image, CRI-O credential provider will be invoked to obtain credentials for pulling
// that image or its mirrors.
//
Copy link
Member

Choose a reason for hiding this comment

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

We're missing the docs for min and max items here, also that it refers to a set:

Suggested change
//
//
// This field is required and must contain between 1 and 50 entries.
// The list is treated as a set, so duplicate entries are not allowed.
//

Comment on lines 108 to 153
const (
// ConditionTypeValidated indicates whether the configuration is failed, or partially valid
ConditionTypeValidated = "Validated"

// ReasonValidationFailed indicates the MatchImages configuration contains invalid patterns
ReasonValidationFailed = "ValidationFailed"

// ReasonConfigurationPartiallyApplied indicates some matchImage entries were ignored due to conflicts
ReasonConfigurationPartiallyApplied = "ConfigurationPartiallyApplied"
)
Copy link
Member

Choose a reason for hiding this comment

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

We should add comprehensive documentation about their meaning and usage:

Suggested change
const (
// ConditionTypeValidated indicates whether the configuration is failed, or partially valid
ConditionTypeValidated = "Validated"
// ReasonValidationFailed indicates the MatchImages configuration contains invalid patterns
ReasonValidationFailed = "ValidationFailed"
// ReasonConfigurationPartiallyApplied indicates some matchImage entries were ignored due to conflicts
ReasonConfigurationPartiallyApplied = "ConfigurationPartiallyApplied"
)
const (
// ConditionTypeValidated is a condition type that indicates whether the CRIOCredentialProviderConfig
// matchImages configuration has been validated successfully.
// When True, all matchImage patterns are valid and have been applied.
// When False, the configuration contains errors (see Reason for details).
// Possible reasons for False status:
// - ValidationFailed: matchImages contains invalid patterns
// - ConfigurationPartiallyApplied: some matchImage entries were ignored due to conflicts
ConditionTypeValidated = "Validated"
// ReasonValidationFailed is a condition reason used with ConditionTypeValidated=False
// to indicate that the matchImages configuration contains one or more invalid registry patterns
// that do not conform to the required format (valid FQDN with optional wildcard, port, and path).
ReasonValidationFailed = "ValidationFailed"
// ReasonConfigurationPartiallyApplied is a condition reason used with ConditionTypeValidated=False
// to indicate that some matchImage entries were ignored due to conflicts or overlapping patterns.
// The condition message will contain details about which entries were ignored and why.
ReasonConfigurationPartiallyApplied = "ConfigurationPartiallyApplied"
)

@QiWang19 QiWang19 force-pushed the creds-provider branch 2 times, most recently from d2a191b to 41bd600 Compare November 4, 2025 20:10
@openshift-ci openshift-ci bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 4, 2025
@QiWang19 QiWang19 changed the title Add CRIOCredentialProviderConfig API OCPNODE-3863: Add CRIOCredentialProviderConfig API Nov 4, 2025
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Nov 4, 2025
@openshift-ci-robot
Copy link

openshift-ci-robot commented Nov 4, 2025

@QiWang19: This pull request references OCPNODE-3863 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.21.0" version, but no target version was set.

In response to this:

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-robot
Copy link

openshift-ci-robot commented Nov 4, 2025

@QiWang19: This pull request references OCPNODE-3863 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.21.0" version, but no target version was set.

In response to this:

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.

Copy link
Member

@saschagrunert saschagrunert left a comment

Choose a reason for hiding this comment

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

Two nits, otherwise LGTM

@QiWang19
Copy link
Member Author

/assign @everettraven

@QiWang19
Copy link
Member Author

@JoelSpeed @everettraven could you review?

- Add feature gate for CRIOCredentialProviderConfig in various feature gate manifests.

Signed-off-by: Qi Wang <[email protected]>
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 14, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

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-ci
Copy link
Contributor

openshift-ci bot commented Nov 14, 2025

@QiWang19: The following tests 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/minor-e2e-upgrade-minor cc9c041 link true /test minor-e2e-upgrade-minor
ci/prow/okd-scos-e2e-aws-ovn cc9c041 link false /test okd-scos-e2e-aws-ovn

Full PR test history. Your PR dashboard.

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.

@QiWang19
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

This pull request introduces a new ClusterScope API resource CRIOCredentialProviderConfig to the OpenShift configuration API (v1alpha1). The change adds the type definitions with spec (matchImages patterns) and status (conditions) fields, registers the types in the API scheme, generates associated deepcopy and OpenAPI documentation, creates CustomResourceDefinition manifests across feature gate variants, and enables the feature via a new feature gate definition with payload manifests.

Changes

Cohort / File(s) Summary
API Type Definitions and Registration
config/v1alpha1/types_crio_credential_provider_config.go, config/v1alpha1/register.go
Introduces CRIOCredentialProviderConfig type with Spec (required matchImages array with validation), Status (conditions array), and adds types to scheme registration via AddKnownTypes
Generated DeepCopy Support
config/v1alpha1/zz_generated.deepcopy.go
Auto-generates DeepCopyInto, DeepCopy, and DeepCopyObject methods for CRIOCredentialProviderConfig, CRIOCredentialProviderConfigList, CRIOCredentialProviderConfigSpec, and CRIOCredentialProviderConfigStatus
Generated Swagger/OpenAPI Documentation
config/v1alpha1/zz_generated.swagger_doc_generated.go, openapi/generated_openapi/zz_generated.openapi.go, openapi/openapi.json
Adds Swagger documentation maps and OpenAPI schema generators for all new CRIOCredentialProviderConfig types, with field descriptions and required field specifications
Generated CRD Manifests (config/v1alpha1)
config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-*...crd.yaml (3 variants), config/v1alpha1/zz_generated.featuregated-crd-manifests* (2 files)
Generates CustomResourceDefinition manifests for CustomNoUpgrade, DevPreviewNoUpgrade, and TechPreviewNoUpgrade variants with OpenAPI v3 schemas defining spec.matchImages (1-50 items, validation rules) and status.conditions (max 4 items)
Feature Gate Definition
features/features.go, features.md
Adds new public FeatureGateCRIOCredentialProviderConfig with Jira component "node", enabled in DevPreviewNoUpgrade and TechPreviewNoUpgrade; documents in features matrix
Test Configuration
config/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml
Adds test suite with valid creation scenario and three invalid scenarios testing matchImages validation (invalid characters, wildcard in path, partial subdomain wildcard)
Payload CRD Manifests
payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-*.crd.yaml (3 variants)
Duplicates CRD definitions across CustomNoUpgrade, DevPreviewNoUpgrade, and TechPreviewNoUpgrade feature gate variants
Payload Feature Gate Manifests
payload-manifests/featuregates/featureGate-*.yaml (6 files)
Adds CRIOCredentialProviderConfig to enabled/disabled lists across Hypershift and SelfManagedHA deployment variants (Default, DevPreviewNoUpgrade, TechPreviewNoUpgrade)
Build Script Update
hack/update-payload-crds.sh
Adds new CRD manifest glob pattern to crd_globs for payload generation

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Key areas requiring attention:
    • config/v1alpha1/types_crio_credential_provider_config.go: Verify XValidation regex rules for matchImages and constraints (maxLength 512, 1-50 items, set type)
    • CRD manifest consistency: Spot-check one CustomNoUpgrade and one DevPreviewNoUpgrade variant to ensure schema alignment across feature gate definitions
    • Feature gate gating strategy: Confirm appropriate enablement only in DevPreviewNoUpgrade and TechPreviewNoUpgrade across all payload manifests
    • Test configuration: Validate that test scenarios properly cover the matchImages validation constraints
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
openapi/generated_openapi/zz_generated.openapi.go (1)

21930-22099: Schemas are structurally sound; double‑check enforcement of documented constraints

The new OpenAPI schemas for:

  • CRIOCredentialProviderConfig / CRIOCredentialProviderConfigList
  • CRIOCredentialProviderConfigSpec (with spec.matchImages)
  • CRIOCredentialProviderConfigStatus (with status.conditions)

are structurally consistent with other config APIs: TypeMeta/ObjectMeta, required spec, list semantics for matchImages (x-kubernetes-list-type: "set"), and condition lists using metav1.Condition with map semantics (x-kubernetes-list-type: "map", x-kubernetes-list-map-keys: ["type"]). Dependencies also look correct.

One thing to verify: the descriptions state constraints that are not encoded in the schema:

  • spec.matchImages “must contain between 1 and 50 entries” and disallows certain wildcard usages, but there’s no MinItems, MaxItems, or string Pattern.
  • status.conditions description mentions “The maximum number of conditions is 4”, but there’s no MaxItems.

If these limits are expected to be enforced at API level (not just by a controller), it may be worth reflecting them in the OpenAPI schema so clients and validation tooling see them. If enforcement is intentionally done only in controller logic, consider confirming the docs and implementation are aligned.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6711368 and cc9c041.

📒 Files selected for processing (24)
  • config/v1alpha1/register.go (1 hunks)
  • config/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml (1 hunks)
  • config/v1alpha1/types_crio_credential_provider_config.go (1 hunks)
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml (1 hunks)
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml (1 hunks)
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml (1 hunks)
  • config/v1alpha1/zz_generated.deepcopy.go (1 hunks)
  • config/v1alpha1/zz_generated.featuregated-crd-manifests.yaml (1 hunks)
  • config/v1alpha1/zz_generated.featuregated-crd-manifests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml (1 hunks)
  • config/v1alpha1/zz_generated.swagger_doc_generated.go (1 hunks)
  • features.md (1 hunks)
  • features/features.go (1 hunks)
  • hack/update-payload-crds.sh (1 hunks)
  • openapi/generated_openapi/zz_generated.openapi.go (2 hunks)
  • openapi/openapi.json (1 hunks)
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml (1 hunks)
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml (1 hunks)
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-Hypershift-Default.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml (1 hunks)
  • payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml
  • hack/update-payload-crds.sh
  • payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml
  • features/features.go
  • payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml
  • payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml
  • features.md
  • payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml
  • config/v1alpha1/zz_generated.swagger_doc_generated.go
  • config/v1alpha1/zz_generated.featuregated-crd-manifests.yaml
  • config/v1alpha1/zz_generated.deepcopy.go
  • payload-manifests/featuregates/featureGate-Hypershift-Default.yaml
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml
  • config/v1alpha1/register.go
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml
  • config/v1alpha1/types_crio_credential_provider_config.go
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml
  • config/v1alpha1/zz_generated.featuregated-crd-manifests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml
  • config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml
  • config/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml
  • openapi/generated_openapi/zz_generated.openapi.go
  • openapi/openapi.json
🪛 YAMLlint (1.37.1)
config/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml

[error] 30-30: duplication of key "initial" in mapping

(key-duplicates)

🔇 Additional comments (26)
config/v1alpha1/zz_generated.featuregated-crd-manifests.yaml (1)

24-45: Verify PR reference consistency.

The CRD entry references PR #1929 for API approval, but this is PR #2557. Confirm that PR #1929 is the correct approval reference or if this should reference the current PR.

config/v1alpha1/zz_generated.featuregated-crd-manifests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml (3)

23-28: LGTM. The description clearly documents compatibility level 4 and the purpose of the resource.


52-112: LGTM. The spec.matchImages schema is well-designed with:

  • Clear constraints (1–50 items, set-type semantics)
  • Comprehensive documentation with examples
  • Regex validation that properly enforces wildcard rules (no global *, no wildcards in port/path)
  • Proper enforcement that wildcards match single domain labels

120-187: LGTM. The status.conditions field uses proper Kubernetes semantics:

  • x-kubernetes-list-type: map with x-kubernetes-list-map-keys: [type] ensures condition uniqueness
  • All required fields properly constrained (reason pattern, status enum)
  • maxItems limit prevents unbounded condition growth
payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml (1)

127-129: LGTM. Feature gate correctly enabled in TechPreviewNoUpgrade.

payload-manifests/featuregates/featureGate-Hypershift-Default.yaml (1)

57-59: LGTM. Feature gate correctly disabled in Default.

hack/update-payload-crds.sh (1)

29-29: LGTM. New glob pattern follows established conventions and correctly captures all CRD variants.

payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml (1)

57-59: LGTM. Feature gate correctly disabled in Default.

features.md (1)

33-33: LGTM. Feature gate documentation is accurate and consistent with manifest configurations.

payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml (1)

112-114: LGTM. Feature gate correctly enabled in DevPreviewNoUpgrade.

payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml (1)

109-111: LGTM!

The CRIOCredentialProviderConfig feature gate is correctly added to the enabled list in alphabetical order.

config/v1alpha1/register.go (1)

43-44: LGTM!

The new types are correctly registered in the API scheme following standard Kubernetes conventions.

payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml (1)

94-96: LGTM!

The feature gate entry is correctly added and properly ordered.

features/features.go (1)

142-148: LGTM!

The feature gate definition follows the established pattern and includes all required metadata.

config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml (1)

5-5: Verify API approval PR reference.

The annotation references PR 1929, but this is PR 2557. Confirm whether PR 1929 is the correct API approval reference or if this should point to the current PR.

config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml (1)

1-195: CRD manifest structure is correct.

This manifest follows the same structure as the TechPreviewNoUpgrade variant. The same verification comments apply regarding the API approval annotation and regex validation.

payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml (1)

1-195: LGTM!

The payload CRD manifest is correctly structured. The verification comments from the generated CRD manifest regarding API approval annotation and regex validation apply here as well.

config/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yaml (1)

29-29: Fix missing list marker.

Line 29 is missing the dash (-) list item marker, causing a YAML syntax error. The static analysis correctly flagged a duplicate "initial" key because the structure is malformed.

Apply this diff:

-    - name: Should reject matchImages with invalid characters
+    - name: Should reject matchImages with invalid characters

Note: The line should begin with spaces followed by a dash, properly indented to match lines 8, 37, and 45.

Likely an incorrect or invalid review comment.

payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yaml (1)

20-195: CRD schema structure looks consistent with the Go type definition

The top-level schema, spec.matchImages constraints (required, 1–50 items, set semantics, string validation) and status.conditions (max 4, map keyed by type) all line up with the CRIOCredentialProviderConfig Go types and kubebuilder markers. I don’t see structural issues in this CRD for the DevPreviewNoUpgrade track.

config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml (1)

20-195: Generated CustomNoUpgrade CRD matches the API schema

This generated CRD mirrors the DevPreview/Go-type schema: same fields, matchImages constraints, and conditions layout. It looks consistent and ready to ship for the CustomNoUpgrade track.

config/v1alpha1/types_crio_credential_provider_config.go (1)

21-153: Overall API shape and annotations look solid

The CRIOCredentialProviderConfig/Spec/Status/List types, kubebuilder markers, OpenShift-specific annotations, and condition constants follow the existing config API patterns and give controllers a clear contract (required spec, validated matchImages set, bounded conditions, documented condition types/reasons). I don’t see structural or maintainability issues in the type layout itself.

payload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yaml (1)

20-195: Payload CRD wiring matches generated schema and Go types

This payload CRD for CustomNoUpgrade correctly exposes the CRIOCredentialProviderConfig API: group/version, names, scope, matchImages list constraints, and status.conditions all match the generated CRD and the Go type definition. No additional structural issues here beyond whatever you decide for the MatchImage validation in the Go type.

config/v1alpha1/zz_generated.deepcopy.go (1)

195-302: Deepcopy implementations for CRIOCredentialProviderConfig are correct and idiomatic

The autogenerated DeepCopy/DeepCopyInto/DeepCopyObject methods for CRIOCredentialProviderConfig, its List, Spec, and Status follow the same patterns as neighboring types (deep-copying ObjectMeta, ListMeta, MatchImages slice, and Conditions slice). They look correct and sufficient for safe use in controllers and tests.

openapi/openapi.json (1)

11938-12034: OpenAPI schema for CRIOCredentialProviderConfig is complete and correctly generated.

All four exported Go types have corresponding schema definitions in openapi.json (CRIOCredentialProviderConfig, CRIOCredentialProviderConfigList, CRIOCredentialProviderConfigSpec, CRIOCredentialProviderConfigStatus). The schema follows Kubernetes conventions with correct type hierarchies, required field declarations, and validation directives.

config/v1alpha1/zz_generated.swagger_doc_generated.go (1)

229-265: LGTM! Auto-generated Swagger documentation is comprehensive and accurate.

The auto-generated Swagger documentation for the new CRIOCredentialProviderConfig types is well-structured and consistent with existing types in the file. The documentation provides clear descriptions, validation constraints, pattern matching rules, and helpful examples—particularly for the matchImages field which includes detailed pattern matching semantics and usage examples.

openapi/generated_openapi/zz_generated.openapi.go (1)

439-448: CRIOCredentialProviderConfig OpenAPI registrations look correct

The four new CRIOCredentialProviderConfig* types are wired into GetOpenAPIDefinitions consistently with neighboring v1alpha1 types (Backup*, ClusterImagePolicy*, etc.). Type names and corresponding schema functions align, so registration looks good.

Comment on lines +56 to +82
// Each entry in matchImages is a pattern which can optionally contain a port and a path.
// Wildcards ('*') are supported for full subdomain labels, such as '*.k8s.io' or 'k8s.*.io',
// and for top-level domains, such as 'k8s.*' (which matches 'k8s.io' or 'k8s.net').
// A global wildcard '*' (matching any domain) is not allowed.
// Wildcards are not allowed in the port or path, nor may they appear in the middle of a hostname label.
// For example, '*.example.com' is valid, but 'example*.*.com' is not.
// Each wildcard matches only a single domain label,
// so '*.io' does **not** match '*.k8s.io'.
//
// A match exists between an image and a matchImage when all of the below are true:
// - Both contain the same number of domain parts and each part matches.
// - The URL path of an matchImages must be a prefix of the target image URL path.
// - If the matchImages contains a port, then the port must match in the image as well.
//
// Example values of matchImages:
// - 123456789.dkr.ecr.us-east-1.amazonaws.com
// - *.azurecr.io
// - gcr.io
// - *.*.registry.io
// - registry.io:8080/path
//
// +kubebuilder:validation:MaxItems=50
// +kubebuilder:validation:MinItems=1
// +listType=set
// +required
MatchImages []MatchImage `json:"matchImages,omitempty"`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Docs say global * is forbidden, but the validation regex currently allows it

The matchImages documentation explicitly states that a global wildcard * (matching any domain) is not allowed, but the MatchImage validation rule:

// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-zA-Z0-9]...))...$')`, ...

still accepts a bare "*" (and variants like "*" with no additional labels), because the first group allows a standalone * without requiring any further domain parts.

That leaves the API in an ambiguous state:

  • A user can submit matchImages: ["*"] and it will pass schema validation.
  • The docs, CRD descriptions, and earlier review discussion all suggest this should be rejected.

To keep the API self‑consistent, you probably want to do one of:

  • Tighten the CEL rule so that "*" (and, if undesired, patterns whose domain portion is solely "*") are rejected, e.g. by combining self != "*" ... with the existing matches(...) expression, or
  • Relax the docs and examples to permit "*" if that is in fact intended to be supported.

Right now, clients cannot reliably reason about what is valid just from the documentation. I’d recommend deciding which behavior you want and aligning both the docs and XValidation rule with that choice.

Also applies to: 84-99



---

🏁 Script executed:

```shell
#!/bin/bash
# Find the CEL validation rule for matchImages field
cat config/v1alpha1/types_crio_credential_provider_config.go | head -100

Length of output: 5501


🏁 Script executed:

#!/bin/bash
# Search for MatchImage type definition and its validation
rg -n "type MatchImage" config/v1alpha1/

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Search for the CEL validation rule pattern
rg -n "XValidation" config/v1alpha1/types_crio_credential_provider_config.go -A 2 -B 2

Length of output: 393


#!/usr/bin/env python3
import re

# The exact regex from the validation rule
pattern = r'^((\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

# Test cases
test_cases = [
    ("*", "bare asterisk (should be FORBIDDEN per docs)"),
    ("*.azurecr.io", "valid single-label wildcard"),
    ("*.*.registry.io", "valid multi-label wildcard"),
    ("gcr.io", "valid plain domain"),
    ("registry.io:8080/path", "valid with port and path"),
    ("123456789.dkr.ecr.us-east-1.amazonaws.com", "valid plain domain example"),
]

print("Testing regex against test cases:\n")
for test_value, description in test_cases:
    try:
        match = re.match(pattern, test_value)
        result = "✓ MATCHES" if match else "✗ DOES NOT MATCH"
        print(f"{result:20} | {test_value:40} | {description}")
    except Exception as e:
        print(f"ERROR: {test_value:40} | {e}")

---

🏁 Script executed:

```shell
#!/bin/bash
# Test the regex pattern to verify if bare "*" matches
python3 << 'EOF'
import re

# The exact regex from the validation rule (unescaped for Python)
pattern = r'^((\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

# Test cases
test_cases = [
    ("*", "bare asterisk (docs say FORBIDDEN)"),
    ("*.azurecr.io", "valid single-label wildcard"),
    ("*.*.registry.io", "valid multi-label wildcard"),
    ("gcr.io", "valid plain domain"),
    ("registry.io:8080/path", "valid with port and path"),
    ("123456789.dkr.ecr.us-east-1.amazonaws.com", "valid plain domain"),
]

print("Regex pattern matching test:\n")
for test_value, description in test_cases:
    match = re.match(pattern, test_value)
    result = "MATCHES" if match else "NO MATCH"
    print(f"{result:12} | {test_value:40} | {description}")
EOF

Length of output: 531


Fix regex to enforce documented constraint: reject bare * wildcard

The regex validation indeed allows bare "*" to pass, contradicting the explicit documentation stating "A global wildcard '*' (matching any domain) is not allowed."

The regex pattern ^((\*|[a-zA-Z0-9]...)(\.(...))*)(:[0-9]+)?(/...)?$ permits a standalone * in the first label position with zero additional domain parts, creating an inconsistency between the validation rule and documented behavior.

Recommended fix:
Add a constraint to the XValidation rule to reject bare *: combine self != "*" with the existing matches(...) expression to align the validation with the documentation.

Also applies to: 84-99

🤖 Prompt for AI Agents
In config/v1alpha1/types_crio_credential_provider_config.go around lines 56-82
(and similarly for the rule at lines 84-99), the current OpenAPI XValidation
regex allows a standalone "*" which contradicts the documented rule that a
global wildcard is not allowed; update the XValidation expression to require
that the value is not exactly "*" in addition to matching the existing regex
(i.e., combine self != "*" with the existing matches(...) condition with a
logical AND), and apply the same change to the second validation block so bare
"*" is rejected in both places.

Comment on lines +104 to +106
- message: invalid matchImages value, must be a valid fully qualified
domain name with optional wildcard, port, and path
rule: self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify matchImages validation regex.

The regex pattern for matchImages validation is complex. Ensure it correctly validates:

  • Full subdomain wildcards (e.g., *.azurecr.io)
  • Multiple wildcard levels (e.g., *.*.registry.io)
  • Port specifications (e.g., :8080)
  • Path prefixes (e.g., /path)
  • Rejection of wildcards in ports/paths
  • Rejection of partial subdomain wildcards (e.g., app*.com)

Consider generating a test to validate the regex against the examples provided in the test file:


🏁 Script executed:

#!/bin/bash
# Verify the regex pattern matches valid examples and rejects invalid ones

# Valid examples from documentation
valid_examples=(
  "123456789.dkr.ecr.us-east-1.amazonaws.com"
  "*.azurecr.io"
  "gcr.io"
  "*.*.registry.io"
  "registry.io:8080/path"
)

# Invalid examples from test cases
invalid_examples=(
  "reg!stry.io"
  "registry.io:8080/pa*th"
  "example.app*.com"
)

regex='^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

echo "Testing valid examples:"
for example in "${valid_examples[@]}"; do
  if echo "$example" | grep -qP "$regex"; then
    echo "$example"
  else
    echo "$example (should match)"
  fi
done

echo -e "\nTesting invalid examples:"
for example in "${invalid_examples[@]}"; do
  if echo "$example" | grep -qP "$regex"; then
    echo "$example (should not match)"
  else
    echo "$example"
  fi
done

Length of output: 293


I need to extract the exact regex from the YAML file and test it properly to diagnose the issue.Now I'll run a script to extract the exact regex from the YAML file and test it properly with Python:


🏁 Script executed:

#!/bin/bash
# Extract the exact regex from the YAML file and test with Python

python3 << 'PYTHON_EOF'
import re
import yaml

# Read the YAML file
yaml_file = "config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml"

try:
    with open(yaml_file, 'r') as f:
        data = yaml.safe_load(f)
    
    # Navigate to find the regex pattern in the spec
    # It should be under spec > validation > rules
    rules = data.get('spec', {}).get('validation', {}).get('rules', [])
    
    for rule in rules:
        if 'rule' in rule:
            print(f"Found rule: {rule['rule']}")
            
            # Extract the regex pattern from the CEL expression
            # The rule is: self.matches('^..regex pattern...$')
            rule_text = rule['rule']
            if 'self.matches(' in rule_text:
                # Extract pattern between single quotes
                start = rule_text.find("'") + 1
                end = rule_text.rfind("'")
                regex_pattern = rule_text[start:end]
                
                print(f"\nExtracted regex pattern:")
                print(repr(regex_pattern))
                print(f"\nActual pattern string:")
                print(regex_pattern)
                
                # Test examples
                valid_examples = [
                    "123456789.dkr.ecr.us-east-1.amazonaws.com",
                    "*.azurecr.io",
                    "gcr.io",
                    "*.*.registry.io",
                    "registry.io:8080/path",
                ]
                
                invalid_examples = [
                    "reg!stry.io",
                    "registry.io:8080/pa*th",
                    "example.app*.com",
                ]
                
                print("\n--- Testing with fullmatch (like CEL matches) ---")
                print("Valid examples (should match):")
                for example in valid_examples:
                    match = re.fullmatch(regex_pattern, example)
                    status = "✓" if match else "✗"
                    print(f"  {status} {example}")
                
                print("\nInvalid examples (should NOT match):")
                for example in invalid_examples:
                    match = re.fullmatch(regex_pattern, example)
                    status = "✓" if not match else "✗"
                    print(f"  {status} {example}")
except FileNotFoundError:
    print(f"File not found: {yaml_file}")
except yaml.YAMLError as e:
    print(f"YAML parsing error: {e}")
except Exception as e:
    print(f"Error: {e}")
    import traceback
    traceback.print_exc()
PYTHON_EOF

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Extract the regex from YAML using simple text tools and test with Python

# First, find the line with the regex pattern
regex_line=$(grep -A 2 "invalid matchImages value" config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml 2>/dev/null | grep "rule:")

if [ -z "$regex_line" ]; then
  echo "Could not find regex line"
  ls -la config/v1alpha1/zz_generated.crd-manifests/ 2>/dev/null || echo "Directory not found"
  exit 1
fi

echo "Found rule line:"
echo "$regex_line"

# Extract the regex pattern between single quotes
regex_pattern=$(echo "$regex_line" | sed -n "s/.*self\.matches('\(.*\)').*/\1/p")

echo -e "\nExtracted regex:"
echo "$regex_pattern"

python3 << 'PYTHON_EOF'
import re
import sys

# Test examples
valid_examples = [
    "123456789.dkr.ecr.us-east-1.amazonaws.com",
    "*.azurecr.io",
    "gcr.io",
    "*.*.registry.io",
    "registry.io:8080/path",
]

invalid_examples = [
    "reg!stry.io",
    "registry.io:8080/pa*th",
    "example.app*.com",
]

regex_pattern = r'^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

print("Testing valid examples (should match):")
for example in valid_examples:
    try:
        match = re.fullmatch(regex_pattern, example)
        status = "✓" if match else "✗"
        print(f"  {status} {example}")
    except Exception as e:
        print(f"  ERROR {example}: {e}")

print("\nTesting invalid examples (should NOT match):")
for example in invalid_examples:
    try:
        match = re.fullmatch(regex_pattern, example)
        status = "✓" if not match else "✗"
        print(f"  {status} {example}")
    except Exception as e:
        print(f"  ERROR {example}: {e}")
PYTHON_EOF

Length of output: 601


🏁 Script executed:

#!/bin/bash
# Test corrected regex pattern with proper escaping

python3 << 'PYTHON_EOF'
import re

# Test examples
valid_examples = [
    "123456789.dkr.ecr.us-east-1.amazonaws.com",
    "*.azurecr.io",
    "gcr.io",
    "*.*.registry.io",
    "registry.io:8080/path",
]

invalid_examples = [
    "reg!stry.io",
    "registry.io:8080/pa*th",
    "example.app*.com",
]

# Current broken regex from YAML (with double backslashes)
broken_regex = r'^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

# Corrected regex - asterisk should not be escaped in character context
corrected_regex = r'^((\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\.(\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

print("=== CURRENT (BROKEN) REGEX ===")
print(f"Pattern: {broken_regex}\n")
print("Testing valid examples (should ALL match):")
valid_count = 0
for example in valid_examples:
    match = re.fullmatch(broken_regex, example)
    status = "✓" if match else "✗"
    if match:
        valid_count += 1
    print(f"  {status} {example}")
print(f"Matched: {valid_count}/{len(valid_examples)}\n")

print("Testing invalid examples (should ALL NOT match):")
invalid_count = 0
for example in invalid_examples:
    match = re.fullmatch(broken_regex, example)
    status = "✓" if not match else "✗"
    if not match:
        invalid_count += 1
    print(f"  {status} {example}")
print(f"Correctly rejected: {invalid_count}/{len(invalid_examples)}\n")

print("=" * 50)
print("\n=== CORRECTED REGEX ===")
print(f"Pattern: {corrected_regex}\n")
print("Testing valid examples (should ALL match):")
valid_count = 0
for example in valid_examples:
    match = re.fullmatch(corrected_regex, example)
    status = "✓" if match else "✗"
    if match:
        valid_count += 1
    print(f"  {status} {example}")
print(f"Matched: {valid_count}/{len(valid_examples)}\n")

print("Testing invalid examples (should ALL NOT match):")
invalid_count = 0
for example in invalid_examples:
    match = re.fullmatch(corrected_regex, example)
    status = "✓" if not match else "✗"
    if not match:
        invalid_count += 1
    print(f"  {status} {example}")
print(f"Correctly rejected: {invalid_count}/{len(invalid_examples)}")
PYTHON_EOF

Length of output: 1008


Fix critical regex escaping error in matchImages validation rule.

The regex pattern has over-escaped backslashes that break all legitimate image registry validation. The pattern uses \\* and \\. when it should use \* and \. (or rely on proper YAML escaping). This causes the validator to reject all valid domains.

Test results:

  • Current regex: 0/5 valid examples pass ✗
  • Fixed regex: 5/5 valid examples pass ✓

Location: Lines 104-106, change the rule from:

rule: self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')

To:

rule: self.matches('^((\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\.(\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')

This removes the extra backslash escaping while preserving correct literal dot (.) and asterisk (*) matching for wildcard domains like *.azurecr.io.

🤖 Prompt for AI Agents
In
config/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yaml
around lines 104-106, the matchImages validation regex is over-escaped (uses \\*
and \\.) which causes valid registries to be rejected; update the rule to use
single backslashes for the regex escapes (replace occurrences of \\* and
\\.`\\.` with `\*` and `\.` in the YAML string so the regex literal matches
wildcard and dot correctly) ensuring the overall pattern remains the same
otherwise.

Comment on lines +104 to +106
- message: invalid matchImages value, must be a valid fully qualified
domain name with optional wildcard, port, and path
rule: self.matches('^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify regex pattern edge cases.

The matchImages validation regex accepts domain labels with optional hyphens (e.g., example-domain.io), wildcards in label positions, and port/path suffixes. Confirm this covers all intended use cases and that no edge cases (e.g., leading/trailing hyphens) slip through.


🏁 Script executed:

#!/bin/bash
# Test the regex pattern against known valid and invalid inputs
REGEX='^((\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\\.(\\*|[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?))*)(:[0-9]+)?(/[-a-zA-Z0-9_/]*)?$'

echo "Testing valid patterns (should all return 'match'):"
for pattern in \
  "registry.io" \
  "*.azurecr.io" \
  "gcr.io" \
  "*.*.registry.io" \
  "registry.io:8080/path" \
  "my-registry.example-domain.io" \
  "a" \
  "*.io" \
  "registry.io:5000"; do
  if [[ "$pattern" =~ $REGEX ]]; then
    echo "$pattern"
  else
    echo "$pattern (unexpected rejection)"
  fi
done

echo ""
echo "Testing invalid patterns (should all be rejected):"
for pattern in \
  "-invalid.io" \
  "invalid-.io" \
  "*.*.io" \
  "*.example*.com" \
  "example.*..com" \
  "*" \
  "registry.io:-8080" \
  "registry.io/path:8080"; do
  if [[ "$pattern" =~ $REGEX ]]; then
    echo "$pattern (unexpected acceptance)"
  else
    echo "$pattern (correctly rejected)"
  fi
done

Length of output: 750


Critical regex validation pattern defect: rejects all standard FQDN formats.

The regex pattern rejects legitimate fully-qualified domain names like registry.io, *.azurecr.io, gcr.io, registry.io:8080/path, and my-registry.example-domain.io. Of 9 valid patterns tested, 8 were unexpectedly rejected—only a single character a matched. This breaks the validation rule for all practical container registry use cases.

The invalid patterns are correctly rejected (leading/trailing hyphens, malformed wildcards, invalid port syntax), confirming the regex structure is partially sound. However, the core domain label matching logic is fundamentally broken and must be fixed before this change can be merged.

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/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants