Skip to content

Commit da5c4a2

Browse files
committed
Add CRIOCredentialProviderConfig API and related
- Add API and feature gate for CRIOCredentialProviderConfig. Signed-off-by: Qi Wang <qiwan@redhat.com>
1 parent bfa868a commit da5c4a2

File tree

26 files changed

+2249
-0
lines changed

26 files changed

+2249
-0
lines changed

config/v1alpha1/register.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
4040
&ImagePolicyList{},
4141
&ClusterImagePolicy{},
4242
&ClusterImagePolicyList{},
43+
&CRIOCredentialProviderConfig{},
44+
&CRIOCredentialProviderConfigList{},
4345
)
4446
metav1.AddToGroupVersion(scheme, GroupVersion)
4547
return nil
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this
2+
name: "CRIOCredentialProviderConfig"
3+
crdName: "criocredentialproviderconfigs.config.openshift.io"
4+
featureGates:
5+
- CRIOCredentialProviderConfig
6+
tests:
7+
onCreate:
8+
- name: Should create a valid CRIOCredentialProviderConfig
9+
initial: |
10+
apiVersion: config.openshift.io/v1alpha1
11+
kind: CRIOCredentialProviderConfig
12+
metadata:
13+
name: cluster
14+
spec:
15+
matchImages:
16+
- 123456789.dkr.ecr.us-east-1.amazonaws.com
17+
- "*.azurecr.io"
18+
- gcr.io
19+
- "*.*.registry.io"
20+
- registry.io:8080/path
21+
expected: |
22+
apiVersion: config.openshift.io/v1alpha1
23+
kind: CRIOCredentialProviderConfig
24+
metadata:
25+
name: cluster
26+
spec:
27+
matchImages:
28+
- 123456789.dkr.ecr.us-east-1.amazonaws.com
29+
- "*.azurecr.io"
30+
- gcr.io
31+
- "*.*.registry.io"
32+
- registry.io:8080/path
33+
- name: Should reject matchImages with invalid characters
34+
initial: |
35+
apiVersion: config.openshift.io/v1alpha1
36+
kind: CRIOCredentialProviderConfig
37+
metadata:
38+
name: cluster
39+
spec:
40+
matchImages:
41+
- "reg!stry.io"
42+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
43+
- name: Should reject matchImages with uppercase hostname
44+
initial: |
45+
apiVersion: config.openshift.io/v1alpha1
46+
kind: CRIOCredentialProviderConfig
47+
metadata:
48+
name: cluster
49+
spec:
50+
matchImages:
51+
- "Registry.COM"
52+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
53+
- name: Should reject matchImages with uppercase in the path
54+
initial: |
55+
apiVersion: config.openshift.io/v1alpha1
56+
kind: CRIOCredentialProviderConfig
57+
metadata:
58+
name: cluster
59+
spec:
60+
matchImages:
61+
- "registry.io/PaTh"
62+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
63+
- name: Should reject matchImages wildcard in the path
64+
initial: |
65+
apiVersion: config.openshift.io/v1alpha1
66+
kind: CRIOCredentialProviderConfig
67+
metadata:
68+
name: cluster
69+
spec:
70+
matchImages:
71+
- "registry.io:8080/pa*th"
72+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
73+
- name: Should reject wildcard for partial subdomains
74+
initial: |
75+
apiVersion: config.openshift.io/v1alpha1
76+
kind: CRIOCredentialProviderConfig
77+
metadata:
78+
name: cluster
79+
spec:
80+
matchImages:
81+
- "example.app*.com"
82+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
83+
- name: Should reject global wildcard '*'
84+
initial: |
85+
apiVersion: config.openshift.io/v1alpha1
86+
kind: CRIOCredentialProviderConfig
87+
metadata:
88+
name: cluster
89+
spec:
90+
matchImages:
91+
- "*"
92+
expectedError: "spec.matchImages[0]: Invalid value: \"string\": global wildcard '*' is not allowed"
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package v1alpha1
2+
3+
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4+
5+
// +genclient
6+
// +genclient:nonNamespaced
7+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
8+
9+
// CRIOCredentialProviderConfig holds cluster-wide singleton resource configurations for CRI-O credential provider, the name of this instance is "cluster". CRI-O credential provider is a binary shipped with CRI-O that provides a way to obtain container image pull credentials from external sources.
10+
// For example, it can be used to fetch mirror registry credentials from secrets resources in the cluster within the same namespace the pod will be running in.
11+
// CRIOCredentialProviderConfig configuration specifies the pod image sources registries that should trigger the CRI-O credential provider execution, which will resolve the CRI-O mirror configurations and obtain the necessary credentials for pod creation.
12+
// Note: Configuration changes will only take effect after the kubelet restarts, which is automatically managed by the cluster during rollout.
13+
//
14+
// The resource is a singleton named "cluster".
15+
//
16+
// Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.
17+
// +kubebuilder:object:root=true
18+
// +kubebuilder:resource:path=criocredentialproviderconfigs,scope=Cluster
19+
// +kubebuilder:subresource:status
20+
// +openshift:api-approved.openshift.io=https://github.com/openshift/api/pull/1929
21+
// +openshift:file-pattern=cvoRunLevel=0000_10,operatorName=config-operator,operatorOrdering=01
22+
// +openshift:enable:FeatureGate=CRIOCredentialProviderConfig
23+
// +openshift:compatibility-gen:level=4
24+
// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="criocredentialproviderconfig is a singleton, .metadata.name must be 'cluster'"
25+
type CRIOCredentialProviderConfig struct {
26+
metav1.TypeMeta `json:",inline"`
27+
28+
// metadata is the standard object's metadata.
29+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
30+
// +optional
31+
metav1.ObjectMeta `json:"metadata"`
32+
33+
// spec defines the desired configuration of the CRI-O Credential Provider.
34+
// This field is required and must be provided when creating the resource.
35+
// +required
36+
Spec CRIOCredentialProviderConfigSpec `json:"spec,omitzero"`
37+
38+
// status represents the current state of the CRIOCredentialProviderConfig.
39+
// When omitted or nil, it indicates that the status has not yet been set by the controller.
40+
// The controller will populate this field with validation conditions and operational state.
41+
// +optional
42+
Status CRIOCredentialProviderConfigStatus `json:"status,omitzero,omitempty"`
43+
}
44+
45+
// CRIOCredentialProviderConfigSpec defines the desired configuration of the CRI-O Credential Provider.
46+
type CRIOCredentialProviderConfigSpec struct {
47+
// matchImages is a required list of string patterns used to determine whether
48+
// the CRI-O credential provider should be invoked for a given image. This list is
49+
// passed to the kubelet CredentialProviderConfig, and if any pattern matches
50+
// the requested image, CRI-O credential provider will be invoked to obtain credentials for pulling
51+
// that image or its mirrors.
52+
// Depending on the platform, the CRI-O credential provider may be installed alongside an existing platform specific provider.
53+
// Conflicts between the existing platform specific provider image match configuration and this list will be handled by
54+
// the following precedence rule: credentials from built-in kubelet providers (e.g., ECR, GCR, ACR) take precedence over those
55+
// from the CRIOCredentialProviderConfig when both match the same image.
56+
// To avoid uncertainty, it is recommended to avoid configuring your private image patterns to overlap with
57+
// existing platform specific provider config(e.g., the entries from https://github.com/openshift/machine-config-operator/blob/main/templates/common/aws/files/etc-kubernetes-credential-providers-ecr-credential-provider.yaml).
58+
// You can check the resource's Status conditions
59+
// to see if any entries were ignored due to exact matches with known built-in provider patterns.
60+
//
61+
// This field is required and must contain between 1 and 50 entries.
62+
// The list is treated as a set, so duplicate entries are not allowed.
63+
//
64+
// For more details, see:
65+
// https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/
66+
// https://github.com/cri-o/crio-credential-provider#architecture
67+
//
68+
// Each entry in matchImages is a pattern which can optionally contain a port and a path. Each entry must be no longer than 512 characters.
69+
// Wildcards ('*') are supported for full subdomain labels, such as '*.k8s.io' or 'k8s.*.io',
70+
// and for top-level domains, such as 'k8s.*' (which matches 'k8s.io' or 'k8s.net').
71+
// A global wildcard '*' (matching any domain) is not allowed.
72+
// Wildcards may replace an entire hostname label (e.g., *.example.com), but they cannot appear within a label (e.g., f*oo.example.com) and are not allowed in the port or path.
73+
// For example, 'example.*.com' is valid, but 'exa*mple.*.com' is not.
74+
// Each wildcard matches only a single domain label,
75+
// so '*.io' does **not** match '*.k8s.io'.
76+
//
77+
// A match exists between an image and a matchImage when all of the below are true:
78+
// Both contain the same number of domain parts and each part matches.
79+
// The URL path of an matchImages must be a prefix of the target image URL path.
80+
// If the matchImages contains a port, then the port must match in the image as well.
81+
//
82+
// Example values of matchImages:
83+
// - 123456789.dkr.ecr.us-east-1.amazonaws.com
84+
// - *.azurecr.io
85+
// - gcr.io
86+
// - *.*.registry.io
87+
// - registry.io:8080/path
88+
//
89+
// +kubebuilder:validation:MaxItems=50
90+
// +kubebuilder:validation:MinItems=1
91+
// +listType=set
92+
// +required
93+
MatchImages []MatchImage `json:"matchImages,omitempty"`
94+
}
95+
96+
// MatchImage is a string pattern used to match container image registry addresses.
97+
// It must be a valid fully qualified domain name with optional wildcard, port, and path.
98+
// The maximum length is 512 characters.
99+
//
100+
// Wildcards ('*') are supported for full subdomain labels and top-level domains.
101+
// Each entry can optionally contain a port (e.g., :8080) and a path (e.g., /path).
102+
// Wildcards are not allowed in the port or path portions.
103+
//
104+
// Examples:
105+
// - "registry.io" - matches exactly registry.io
106+
// - "*.azurecr.io" - matches any single subdomain of azurecr.io
107+
// - "registry.io:8080/path" - matches with specific port and path prefix
108+
//
109+
// +kubebuilder:validation:MaxLength=512
110+
// +kubebuilder:validation:XValidation:rule="self != '*'",message="global wildcard '*' is not allowed"
111+
// +kubebuilder:validation:XValidation:rule=`self.matches('^((\\*|[a-z0-9]([a-z0-9-]*[a-z0-9])?)(\\.(\\*|[a-z0-9]([a-z0-9-]*[a-z0-9])?))*)(:[0-9]+)?(/[-a-z0-9_/]*)?$')`,message="invalid matchImages value, must be a valid fully qualified domain name in lowercase with optional wildcard, port, and path"
112+
type MatchImage string
113+
114+
// +k8s:deepcopy-gen=true
115+
// CRIOCredentialProviderConfigStatus defines the observed state of CRIOCredentialProviderConfig
116+
// +kubebuilder:validation:MinProperties=1
117+
type CRIOCredentialProviderConfigStatus struct {
118+
// conditions represent the latest available observations of the configuration state.
119+
// When omitted, it indicates that no conditions have been reported yet.
120+
// The maximum number of conditions is 16.
121+
// Conditions are stored as a map keyed by condition type, ensuring uniqueness.
122+
//
123+
// Expected condition types include:
124+
// "Validated": indicates whether the matchImages configuration is valid
125+
// +optional
126+
// +kubebuilder:validation:MaxItems=16
127+
// +kubebuilder:validation:MinItems=1
128+
// +listType=map
129+
// +listMapKey=type
130+
Conditions []metav1.Condition `json:"conditions,omitempty"`
131+
}
132+
133+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
134+
135+
// CRIOCredentialProviderConfigList contains a list of CRIOCredentialProviderConfig resources
136+
//
137+
// Compatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.
138+
// +openshift:compatibility-gen:level=4
139+
type CRIOCredentialProviderConfigList struct {
140+
metav1.TypeMeta `json:",inline"`
141+
142+
// metadata is the standard list's metadata.
143+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
144+
metav1.ListMeta `json:"metadata"`
145+
146+
Items []CRIOCredentialProviderConfig `json:"items"`
147+
}
148+
149+
const (
150+
// ConditionTypeValidated is a condition type that indicates whether the CRIOCredentialProviderConfig
151+
// matchImages configuration has been validated successfully.
152+
// When True, all matchImage patterns are valid and have been applied.
153+
// When False, the configuration contains errors (see Reason for details).
154+
// Possible reasons for False status:
155+
// - ValidationFailed: matchImages contains invalid patterns
156+
// - ConfigurationPartiallyApplied: some matchImage entries were ignored due to conflicts
157+
ConditionTypeValidated = "Validated"
158+
159+
// ReasonValidationFailed is a condition reason used with ConditionTypeValidated=False
160+
// to indicate that the matchImages configuration contains one or more invalid registry patterns
161+
// that do not conform to the required format (valid FQDN with optional wildcard, port, and path).
162+
ReasonValidationFailed = "ValidationFailed"
163+
164+
// ReasonConfigurationPartiallyApplied is a condition reason used with ConditionTypeValidated=False
165+
// to indicate that some matchImage entries were ignored due to conflicts or overlapping patterns.
166+
// The condition message will contain details about which entries were ignored and why.
167+
ReasonConfigurationPartiallyApplied = "ConfigurationPartiallyApplied"
168+
169+
// ConditionTypeMachineConfigRendered is a condition type that indicates whether
170+
// the CRIOCredentialProviderConfig has been successfully rendered into a
171+
// MachineConfig object.
172+
// When True, the corresponding MachineConfig is present in the cluster.
173+
// When False, rendering failed.
174+
ConditionTypeMachineConfigRendered = "MachineConfigRendered"
175+
176+
// ReasonMachineConfigRenderingSucceeded is a condition reason used with ConditionTypeMachineConfigRendered=True
177+
// to indicate that the MachineConfig was successfully created/updated in the API server.
178+
ReasonMachineConfigRenderingSucceeded = "MachineConfigRenderingSucceeded"
179+
180+
// ReasonMachineConfigRenderingFailed is a condition reason used with ConditionTypeMachineConfigRendered=False
181+
// to indicate that the MachineConfig creation/update failed.
182+
// The condition message will contain details about the failure.
183+
ReasonMachineConfigRenderingFailed = "MachineConfigRenderingFailed"
184+
)

0 commit comments

Comments
 (0)