Skip to content

Commit fe0b8ca

Browse files
committed
Promote OCIRepository API to v1 (GA)
Signed-off-by: Stefan Prodan <[email protected]>
1 parent e253855 commit fe0b8ca

29 files changed

+3229
-395
lines changed

PROJECT

+3
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ resources:
4040
- group: source
4141
kind: Bucket
4242
version: v1
43+
- group: source
44+
kind: OCIRepository
45+
version: v1
4346
version: "2"

README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ and is a core component of the [GitOps toolkit](https://fluxcd.io/flux/component
1616

1717
## APIs
1818

19-
| Kind | API Version |
20-
|-------------------------------------------------------|------------------------------------|
21-
| [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` |
22-
| [OCIRepository](docs/spec/v1beta2/ocirepositories.md) | `source.toolkit.fluxcd.io/v1beta2` |
23-
| [HelmRepository](docs/spec/v1/helmrepositories.md) | `source.toolkit.fluxcd.io/v1` |
24-
| [HelmChart](docs/spec/v1/helmcharts.md) | `source.toolkit.fluxcd.io/v1` |
25-
| [Bucket](docs/spec/v1/buckets.md) | `source.toolkit.fluxcd.io/v1` |
19+
| Kind | API Version |
20+
|----------------------------------------------------|-------------------------------|
21+
| [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` |
22+
| [OCIRepository](docs/spec/v1/ocirepositories.md) | `source.toolkit.fluxcd.io/v1` |
23+
| [HelmRepository](docs/spec/v1/helmrepositories.md) | `source.toolkit.fluxcd.io/v1` |
24+
| [HelmChart](docs/spec/v1/helmcharts.md) | `source.toolkit.fluxcd.io/v1` |
25+
| [Bucket](docs/spec/v1/buckets.md) | `source.toolkit.fluxcd.io/v1` |
2626

2727
## Features
2828

api/go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ require (
2222
github.com/modern-go/reflect2 v1.0.2 // indirect
2323
github.com/spf13/pflag v1.0.6 // indirect
2424
github.com/x448/float16 v0.8.4 // indirect
25-
golang.org/x/net v0.39.0 // indirect
26-
golang.org/x/text v0.24.0 // indirect
25+
golang.org/x/net v0.40.0 // indirect
26+
golang.org/x/text v0.25.0 // indirect
2727
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
2828
gopkg.in/inf.v0 v0.9.1 // indirect
2929
k8s.io/api v0.33.0 // indirect

api/go.sum

+6-6
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,20 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
6464
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6565
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6666
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
67-
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
68-
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
67+
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
68+
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
6969
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7070
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7171
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7272
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
7373
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
7474
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
75-
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
76-
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
75+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
76+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
7777
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
7878
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
79-
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
80-
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
79+
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
80+
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
8181
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
8282
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
8383
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=

api/v1/ocirepository_types.go

+313
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/*
2+
Copyright 2025 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1
18+
19+
import (
20+
"time"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
24+
"github.com/fluxcd/pkg/apis/meta"
25+
)
26+
27+
const (
28+
// OCIRepositoryKind is the string representation of an OCIRepository.
29+
OCIRepositoryKind = "OCIRepository"
30+
31+
// OCIRepositoryPrefix is the prefix used for OCIRepository URLs.
32+
OCIRepositoryPrefix = "oci://"
33+
34+
// GenericOCIProvider provides support for authentication using static credentials
35+
// for any OCI compatible API such as Docker Registry, GitHub Container Registry,
36+
// Docker Hub, Quay, etc.
37+
GenericOCIProvider string = "generic"
38+
39+
// AmazonOCIProvider provides support for OCI authentication using AWS IRSA.
40+
AmazonOCIProvider string = "aws"
41+
42+
// GoogleOCIProvider provides support for OCI authentication using GCP workload identity.
43+
GoogleOCIProvider string = "gcp"
44+
45+
// AzureOCIProvider provides support for OCI authentication using a Azure Service Principal,
46+
// Managed Identity or Shared Key.
47+
AzureOCIProvider string = "azure"
48+
49+
// OCILayerExtract defines the operation type for extracting the content from an OCI artifact layer.
50+
OCILayerExtract = "extract"
51+
52+
// OCILayerCopy defines the operation type for copying the content from an OCI artifact layer.
53+
OCILayerCopy = "copy"
54+
)
55+
56+
// OCIRepositorySpec defines the desired state of OCIRepository
57+
type OCIRepositorySpec struct {
58+
// URL is a reference to an OCI artifact repository hosted
59+
// on a remote container registry.
60+
// +kubebuilder:validation:Pattern="^oci://.*$"
61+
// +required
62+
URL string `json:"url"`
63+
64+
// The OCI reference to pull and monitor for changes,
65+
// defaults to the latest tag.
66+
// +optional
67+
Reference *OCIRepositoryRef `json:"ref,omitempty"`
68+
69+
// LayerSelector specifies which layer should be extracted from the OCI artifact.
70+
// When not specified, the first layer found in the artifact is selected.
71+
// +optional
72+
LayerSelector *OCILayerSelector `json:"layerSelector,omitempty"`
73+
74+
// The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'.
75+
// When not specified, defaults to 'generic'.
76+
// +kubebuilder:validation:Enum=generic;aws;azure;gcp
77+
// +kubebuilder:default:=generic
78+
// +optional
79+
Provider string `json:"provider,omitempty"`
80+
81+
// SecretRef contains the secret name containing the registry login
82+
// credentials to resolve image metadata.
83+
// The secret must be of type kubernetes.io/dockerconfigjson.
84+
// +optional
85+
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
86+
87+
// Verify contains the secret name containing the trusted public keys
88+
// used to verify the signature and specifies which provider to use to check
89+
// whether OCI image is authentic.
90+
// +optional
91+
Verify *OCIRepositoryVerification `json:"verify,omitempty"`
92+
93+
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
94+
// the image pull if the service account has attached pull secrets. For more information:
95+
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account
96+
// +optional
97+
ServiceAccountName string `json:"serviceAccountName,omitempty"`
98+
99+
// CertSecretRef can be given the name of a Secret containing
100+
// either or both of
101+
//
102+
// - a PEM-encoded client certificate (`tls.crt`) and private
103+
// key (`tls.key`);
104+
// - a PEM-encoded CA certificate (`ca.crt`)
105+
//
106+
// and whichever are supplied, will be used for connecting to the
107+
// registry. The client cert and key are useful if you are
108+
// authenticating with a certificate; the CA cert is useful if
109+
// you are using a self-signed server certificate. The Secret must
110+
// be of type `Opaque` or `kubernetes.io/tls`.
111+
//
112+
// Note: Support for the `caFile`, `certFile` and `keyFile` keys have
113+
// been deprecated.
114+
// +optional
115+
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
116+
117+
// ProxySecretRef specifies the Secret containing the proxy configuration
118+
// to use while communicating with the container registry.
119+
// +optional
120+
ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
121+
122+
// Interval at which the OCIRepository URL is checked for updates.
123+
// This interval is approximate and may be subject to jitter to ensure
124+
// efficient use of resources.
125+
// +kubebuilder:validation:Type=string
126+
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
127+
// +required
128+
Interval metav1.Duration `json:"interval"`
129+
130+
// The timeout for remote OCI Repository operations like pulling, defaults to 60s.
131+
// +kubebuilder:default="60s"
132+
// +kubebuilder:validation:Type=string
133+
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m))+$"
134+
// +optional
135+
Timeout *metav1.Duration `json:"timeout,omitempty"`
136+
137+
// Ignore overrides the set of excluded patterns in the .sourceignore format
138+
// (which is the same as .gitignore). If not provided, a default will be used,
139+
// consult the documentation for your version to find out what those are.
140+
// +optional
141+
Ignore *string `json:"ignore,omitempty"`
142+
143+
// Insecure allows connecting to a non-TLS HTTP container registry.
144+
// +optional
145+
Insecure bool `json:"insecure,omitempty"`
146+
147+
// This flag tells the controller to suspend the reconciliation of this source.
148+
// +optional
149+
Suspend bool `json:"suspend,omitempty"`
150+
}
151+
152+
// OCIRepositoryRef defines the image reference for the OCIRepository's URL
153+
type OCIRepositoryRef struct {
154+
// Digest is the image digest to pull, takes precedence over SemVer.
155+
// The value should be in the format 'sha256:<HASH>'.
156+
// +optional
157+
Digest string `json:"digest,omitempty"`
158+
159+
// SemVer is the range of tags to pull selecting the latest within
160+
// the range, takes precedence over Tag.
161+
// +optional
162+
SemVer string `json:"semver,omitempty"`
163+
164+
// SemverFilter is a regex pattern to filter the tags within the SemVer range.
165+
// +optional
166+
SemverFilter string `json:"semverFilter,omitempty"`
167+
168+
// Tag is the image tag to pull, defaults to latest.
169+
// +optional
170+
Tag string `json:"tag,omitempty"`
171+
}
172+
173+
// OCILayerSelector specifies which layer should be extracted from an OCI Artifact
174+
type OCILayerSelector struct {
175+
// MediaType specifies the OCI media type of the layer
176+
// which should be extracted from the OCI Artifact. The
177+
// first layer matching this type is selected.
178+
// +optional
179+
MediaType string `json:"mediaType,omitempty"`
180+
181+
// Operation specifies how the selected layer should be processed.
182+
// By default, the layer compressed content is extracted to storage.
183+
// When the operation is set to 'copy', the layer compressed content
184+
// is persisted to storage as it is.
185+
// +kubebuilder:validation:Enum=extract;copy
186+
// +optional
187+
Operation string `json:"operation,omitempty"`
188+
}
189+
190+
// OCIRepositoryStatus defines the observed state of OCIRepository
191+
type OCIRepositoryStatus struct {
192+
// ObservedGeneration is the last observed generation.
193+
// +optional
194+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
195+
196+
// Conditions holds the conditions for the OCIRepository.
197+
// +optional
198+
Conditions []metav1.Condition `json:"conditions,omitempty"`
199+
200+
// URL is the download link for the artifact output of the last OCI Repository sync.
201+
// +optional
202+
URL string `json:"url,omitempty"`
203+
204+
// Artifact represents the output of the last successful OCI Repository sync.
205+
// +optional
206+
Artifact *Artifact `json:"artifact,omitempty"`
207+
208+
// ContentConfigChecksum is a checksum of all the configurations related to
209+
// the content of the source artifact:
210+
// - .spec.ignore
211+
// - .spec.layerSelector
212+
// observed in .status.observedGeneration version of the object. This can
213+
// be used to determine if the content configuration has changed and the
214+
// artifact needs to be rebuilt.
215+
// It has the format of `<algo>:<checksum>`, for example: `sha256:<checksum>`.
216+
//
217+
// Deprecated: Replaced with explicit fields for observed artifact content
218+
// config in the status.
219+
// +optional
220+
ContentConfigChecksum string `json:"contentConfigChecksum,omitempty"`
221+
222+
// ObservedIgnore is the observed exclusion patterns used for constructing
223+
// the source artifact.
224+
// +optional
225+
ObservedIgnore *string `json:"observedIgnore,omitempty"`
226+
227+
// ObservedLayerSelector is the observed layer selector used for constructing
228+
// the source artifact.
229+
// +optional
230+
ObservedLayerSelector *OCILayerSelector `json:"observedLayerSelector,omitempty"`
231+
232+
meta.ReconcileRequestStatus `json:",inline"`
233+
}
234+
235+
const (
236+
// OCIPullFailedReason signals that a pull operation failed.
237+
OCIPullFailedReason string = "OCIArtifactPullFailed"
238+
239+
// OCILayerOperationFailedReason signals that an OCI layer operation failed.
240+
OCILayerOperationFailedReason string = "OCIArtifactLayerOperationFailed"
241+
)
242+
243+
// GetConditions returns the status conditions of the object.
244+
func (in OCIRepository) GetConditions() []metav1.Condition {
245+
return in.Status.Conditions
246+
}
247+
248+
// SetConditions sets the status conditions on the object.
249+
func (in *OCIRepository) SetConditions(conditions []metav1.Condition) {
250+
in.Status.Conditions = conditions
251+
}
252+
253+
// GetRequeueAfter returns the duration after which the OCIRepository must be
254+
// reconciled again.
255+
func (in OCIRepository) GetRequeueAfter() time.Duration {
256+
return in.Spec.Interval.Duration
257+
}
258+
259+
// GetArtifact returns the latest Artifact from the OCIRepository if present in
260+
// the status sub-resource.
261+
func (in *OCIRepository) GetArtifact() *Artifact {
262+
return in.Status.Artifact
263+
}
264+
265+
// GetLayerMediaType returns the media type layer selector if found in spec.
266+
func (in *OCIRepository) GetLayerMediaType() string {
267+
if in.Spec.LayerSelector == nil {
268+
return ""
269+
}
270+
271+
return in.Spec.LayerSelector.MediaType
272+
}
273+
274+
// GetLayerOperation returns the layer selector operation (defaults to extract).
275+
func (in *OCIRepository) GetLayerOperation() string {
276+
if in.Spec.LayerSelector == nil || in.Spec.LayerSelector.Operation == "" {
277+
return OCILayerExtract
278+
}
279+
280+
return in.Spec.LayerSelector.Operation
281+
}
282+
283+
// +genclient
284+
// +kubebuilder:storageversion
285+
// +kubebuilder:object:root=true
286+
// +kubebuilder:resource:shortName=ocirepo
287+
// +kubebuilder:subresource:status
288+
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
289+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
290+
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
291+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
292+
293+
// OCIRepository is the Schema for the ocirepositories API
294+
type OCIRepository struct {
295+
metav1.TypeMeta `json:",inline"`
296+
metav1.ObjectMeta `json:"metadata,omitempty"`
297+
298+
Spec OCIRepositorySpec `json:"spec,omitempty"`
299+
// +kubebuilder:default={"observedGeneration":-1}
300+
Status OCIRepositoryStatus `json:"status,omitempty"`
301+
}
302+
303+
// OCIRepositoryList contains a list of OCIRepository
304+
// +kubebuilder:object:root=true
305+
type OCIRepositoryList struct {
306+
metav1.TypeMeta `json:",inline"`
307+
metav1.ListMeta `json:"metadata,omitempty"`
308+
Items []OCIRepository `json:"items"`
309+
}
310+
311+
func init() {
312+
SchemeBuilder.Register(&OCIRepository{}, &OCIRepositoryList{})
313+
}

0 commit comments

Comments
 (0)