-
Notifications
You must be signed in to change notification settings - Fork 580
OCPNODE-3863: Add CRIOCredentialProviderConfig API #2557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Skipping CI for Draft Pull Request. |
|
Hello @QiWang19! Some important instructions when contributing to openshift/api: |
c00c122 to
1db4267
Compare
|
/retest-required |
8805169 to
108219f
Compare
|
/test integration |
108219f to
066bf58
Compare
|
PR has been reviewed by claude code /api-review. It helped run |
|
|
||
| // +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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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| // status represents the current state of the CRIOCredentialProviderConfig. | ||
| // +optional | ||
| Status *CRIOCredentialProviderConfigStatus `json:"status,omitempty"` |
There was a problem hiding this comment.
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.
| // 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"` |
| // 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"` |
There was a problem hiding this comment.
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
| // 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"` |
| // spec defines the desired configuration of the CRIO Credential Provider. | ||
| // +required | ||
| Spec CRIOCredentialProviderConfigSpec `json:"spec,omitzero"` |
There was a problem hiding this comment.
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.
| // 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. | ||
| // |
There was a problem hiding this comment.
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:
| // | |
| // | |
| // 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. | |
| // |
| 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" | ||
| ) |
There was a problem hiding this comment.
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:
| 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" | |
| ) |
d2a191b to
41bd600
Compare
|
@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. |
|
@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. |
41bd600 to
e7df906
Compare
saschagrunert
left a comment
There was a problem hiding this 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
|
/assign @everettraven |
|
@JoelSpeed @everettraven could you review? |
- Add feature gate for CRIOCredentialProviderConfig in various feature gate manifests. Signed-off-by: Qi Wang <[email protected]>
e7df906 to
cc9c041
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: saschagrunert 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 |
|
@QiWang19: The following tests failed, say
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. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughThis pull request introduces a new ClusterScope API resource Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
✨ Finishing touches
🧪 Generate unit tests (beta)
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 Comment |
There was a problem hiding this 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 constraintsThe new OpenAPI schemas for:
CRIOCredentialProviderConfig/CRIOCredentialProviderConfigListCRIOCredentialProviderConfigSpec(withspec.matchImages)CRIOCredentialProviderConfigStatus(withstatus.conditions)are structurally consistent with other config APIs: TypeMeta/ObjectMeta, required
spec, list semantics formatchImages(x-kubernetes-list-type: "set"), and condition lists usingmetav1.Conditionwith 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 noMinItems,MaxItems, or stringPattern.status.conditionsdescription mentions “The maximum number of conditions is 4”, but there’s noMaxItems.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
📒 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.yamlhack/update-payload-crds.shpayload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yamlfeatures/features.gopayload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yamlpayload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yamlconfig/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yamlfeatures.mdpayload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yamlconfig/v1alpha1/zz_generated.swagger_doc_generated.goconfig/v1alpha1/zz_generated.featuregated-crd-manifests.yamlconfig/v1alpha1/zz_generated.deepcopy.gopayload-manifests/featuregates/featureGate-Hypershift-Default.yamlpayload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yamlconfig/v1alpha1/register.goconfig/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yamlconfig/v1alpha1/types_crio_credential_provider_config.gopayload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-DevPreviewNoUpgrade.crd.yamlconfig/v1alpha1/zz_generated.featuregated-crd-manifests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yamlconfig/v1alpha1/zz_generated.crd-manifests/0000_10_config-operator_01_criocredentialproviderconfigs-CustomNoUpgrade.crd.yamlpayload-manifests/crds/0000_10_config-operator_01_criocredentialproviderconfigs-TechPreviewNoUpgrade.crd.yamlconfig/v1alpha1/tests/criocredentialproviderconfigs.config.openshift.io/CRIOCredentialProviderConfig.yamlopenapi/generated_openapi/zz_generated.openapi.goopenapi/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. Thespec.matchImagesschema 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. Thestatus.conditionsfield uses proper Kubernetes semantics:
x-kubernetes-list-type: mapwithx-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 charactersNote: 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 definitionThe top-level schema,
spec.matchImagesconstraints (required, 1–50 items, set semantics, string validation) andstatus.conditions(max 4, map keyed by type) all line up with theCRIOCredentialProviderConfigGo 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 schemaThis 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 solidThe 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, validatedmatchImagesset, 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 typesThis payload CRD for CustomNoUpgrade correctly exposes the CRIOCredentialProviderConfig API: group/version, names, scope,
matchImageslist constraints, andstatus.conditionsall 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 idiomaticThe 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
matchImagesfield which includes detailed pattern matching semantics and usage examples.openapi/generated_openapi/zz_generated.openapi.go (1)
439-448: CRIOCredentialProviderConfig OpenAPI registrations look correctThe four new CRIOCredentialProviderConfig* types are wired into
GetOpenAPIDefinitionsconsistently with neighboring v1alpha1 types (Backup*, ClusterImagePolicy*, etc.). Type names and corresponding schema functions align, so registration looks good.
| // 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"` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 combiningself != "*" ...with the existingmatches(...)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 2Length 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.
| - 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_/]*)?$') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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
doneLength 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_EOFLength 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_EOFLength 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_EOFLength 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.
| - 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_/]*)?$') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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
doneLength 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.
enhancement: OCPNODE-3753: CRIOCredentialProviderConfig for Namespace-Scoped Mirror Authentication enhancements#1861
Add CRIOCredentialProviderConfig API setting configs https://github.com/cri-o/crio-credential-provider for retrieving mirror pull secrets
Add feature gate for CRIOCredentialProviderConfig feature gate.