Skip to content

Commit 1db1626

Browse files
authored
Merge pull request #788 from fluxcd/oci
[RFC-0003] Implement OCIRepository reconciliation
2 parents b1a4949 + 94e98ee commit 1db1626

29 files changed

+5215
-77
lines changed

Makefile

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ LIBGIT2_TAG ?= v0.2.0
99
# Allows for defining additional Go test args, e.g. '-tags integration'.
1010
GO_TEST_ARGS ?= -race
1111

12+
# Allows for filtering tests based on the specified prefix
13+
GO_TEST_PREFIX ?=
14+
1215
# Allows for defining additional Docker buildx arguments,
1316
# e.g. '--push'.
1417
BUILD_ARGS ?=
@@ -69,7 +72,7 @@ build: check-deps $(LIBGIT2) ## Build manager binary
6972
go build $(GO_STATIC_FLAGS) -o $(BUILD_DIR)/bin/manager main.go
7073

7174
KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)"
72-
test: $(LIBGIT2) install-envtest test-api check-deps ## Run tests
75+
test: $(LIBGIT2) install-envtest test-api check-deps ## Run all tests
7376
HTTPS_PROXY="" HTTP_PROXY="" \
7477
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \
7578
GIT_CONFIG_GLOBAL=/dev/null \
@@ -78,6 +81,15 @@ test: $(LIBGIT2) install-envtest test-api check-deps ## Run tests
7881
$(GO_TEST_ARGS) \
7982
-coverprofile cover.out
8083

84+
test-ctrl: $(LIBGIT2) install-envtest test-api check-deps ## Run controller tests
85+
HTTPS_PROXY="" HTTP_PROXY="" \
86+
KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \
87+
GIT_CONFIG_GLOBAL=/dev/null \
88+
go test $(GO_STATIC_FLAGS) \
89+
-run "^$(GO_TEST_PREFIX).*" \
90+
-v ./controllers \
91+
-coverprofile cover.out
92+
8193
check-deps:
8294
ifeq ($(shell uname -s),Darwin)
8395
if ! command -v pkg-config &> /dev/null; then echo "pkg-config is required"; exit 1; fi

PROJECT

+3
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ resources:
2525
- group: source
2626
kind: Bucket
2727
version: v1beta1
28+
- group: source
29+
kind: OCIRepository
30+
version: v1beta2
2831
version: "2"

api/v1beta2/artifact_types.go

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ type Artifact struct {
5454
// Size is the number of bytes in the file.
5555
// +optional
5656
Size *int64 `json:"size,omitempty"`
57+
58+
// Metadata holds upstream information such as OCI annotations.
59+
// +optional
60+
Metadata map[string]string `json:"metadata,omitempty"`
5761
}
5862

5963
// HasRevision returns if the given revision matches the current Revision of

api/v1beta2/ocirepository_types.go

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
Copyright 2022 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 v1beta2
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 a 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+
50+
// OCIRepositorySpec defines the desired state of OCIRepository
51+
type OCIRepositorySpec struct {
52+
// URL is a reference to an OCI artifact repository hosted
53+
// on a remote container registry.
54+
// +kubebuilder:validation:Pattern="^oci://.*$"
55+
// +required
56+
URL string `json:"url"`
57+
58+
// The OCI reference to pull and monitor for changes,
59+
// defaults to the latest tag.
60+
// +optional
61+
Reference *OCIRepositoryRef `json:"ref,omitempty"`
62+
63+
// The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'.
64+
// When not specified, defaults to 'generic'.
65+
// +kubebuilder:validation:Enum=generic;aws;azure;gcp
66+
// +kubebuilder:default:=generic
67+
// +optional
68+
Provider string `json:"provider,omitempty"`
69+
70+
// SecretRef contains the secret name containing the registry login
71+
// credentials to resolve image metadata.
72+
// The secret must be of type kubernetes.io/dockerconfigjson.
73+
// +optional
74+
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
75+
76+
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
77+
// the image pull if the service account has attached pull secrets. For more information:
78+
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account
79+
// +optional
80+
ServiceAccountName string `json:"serviceAccountName,omitempty"`
81+
82+
// CertSecretRef can be given the name of a secret containing
83+
// either or both of
84+
//
85+
// - a PEM-encoded client certificate (`certFile`) and private
86+
// key (`keyFile`);
87+
// - a PEM-encoded CA certificate (`caFile`)
88+
//
89+
// and whichever are supplied, will be used for connecting to the
90+
// registry. The client cert and key are useful if you are
91+
// authenticating with a certificate; the CA cert is useful if
92+
// you are using a self-signed server certificate.
93+
// +optional
94+
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
95+
96+
// The interval at which to check for image updates.
97+
// +required
98+
Interval metav1.Duration `json:"interval"`
99+
100+
// The timeout for remote OCI Repository operations like pulling, defaults to 60s.
101+
// +kubebuilder:default="60s"
102+
// +optional
103+
Timeout *metav1.Duration `json:"timeout,omitempty"`
104+
105+
// Ignore overrides the set of excluded patterns in the .sourceignore format
106+
// (which is the same as .gitignore). If not provided, a default will be used,
107+
// consult the documentation for your version to find out what those are.
108+
// +optional
109+
Ignore *string `json:"ignore,omitempty"`
110+
111+
// This flag tells the controller to suspend the reconciliation of this source.
112+
// +optional
113+
Suspend bool `json:"suspend,omitempty"`
114+
}
115+
116+
// OCIRepositoryRef defines the image reference for the OCIRepository's URL
117+
type OCIRepositoryRef struct {
118+
// Digest is the image digest to pull, takes precedence over SemVer.
119+
// The value should be in the format 'sha256:<HASH>'.
120+
// +optional
121+
Digest string `json:"digest,omitempty"`
122+
123+
// SemVer is the range of tags to pull selecting the latest within
124+
// the range, takes precedence over Tag.
125+
// +optional
126+
SemVer string `json:"semver,omitempty"`
127+
128+
// Tag is the image tag to pull, defaults to latest.
129+
// +optional
130+
Tag string `json:"tag,omitempty"`
131+
}
132+
133+
// OCIRepositoryVerification verifies the authenticity of an OCI Artifact
134+
type OCIRepositoryVerification struct {
135+
// Provider specifies the technology used to sign the OCI Artifact.
136+
// +kubebuilder:validation:Enum=cosign
137+
Provider string `json:"provider"`
138+
139+
// SecretRef specifies the Kubernetes Secret containing the
140+
// trusted public keys.
141+
SecretRef meta.LocalObjectReference `json:"secretRef"`
142+
}
143+
144+
// OCIRepositoryStatus defines the observed state of OCIRepository
145+
type OCIRepositoryStatus struct {
146+
// ObservedGeneration is the last observed generation.
147+
// +optional
148+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
149+
150+
// Conditions holds the conditions for the OCIRepository.
151+
// +optional
152+
Conditions []metav1.Condition `json:"conditions,omitempty"`
153+
154+
// URL is the download link for the artifact output of the last OCI Repository sync.
155+
// +optional
156+
URL string `json:"url,omitempty"`
157+
158+
// Artifact represents the output of the last successful OCI Repository sync.
159+
// +optional
160+
Artifact *Artifact `json:"artifact,omitempty"`
161+
162+
meta.ReconcileRequestStatus `json:",inline"`
163+
}
164+
165+
const (
166+
// OCIPullFailedReason signals that a pull operation failed.
167+
OCIPullFailedReason string = "OCIArtifactPullFailed"
168+
169+
// OCILayerOperationFailedReason signals that an OCI layer operation failed.
170+
OCILayerOperationFailedReason string = "OCIArtifactLayerOperationFailed"
171+
)
172+
173+
// GetConditions returns the status conditions of the object.
174+
func (in OCIRepository) GetConditions() []metav1.Condition {
175+
return in.Status.Conditions
176+
}
177+
178+
// SetConditions sets the status conditions on the object.
179+
func (in *OCIRepository) SetConditions(conditions []metav1.Condition) {
180+
in.Status.Conditions = conditions
181+
}
182+
183+
// GetRequeueAfter returns the duration after which the OCIRepository must be
184+
// reconciled again.
185+
func (in OCIRepository) GetRequeueAfter() time.Duration {
186+
return in.Spec.Interval.Duration
187+
}
188+
189+
// GetArtifact returns the latest Artifact from the OCIRepository if present in
190+
// the status sub-resource.
191+
func (in *OCIRepository) GetArtifact() *Artifact {
192+
return in.Status.Artifact
193+
}
194+
195+
// +genclient
196+
// +genclient:Namespaced
197+
// +kubebuilder:storageversion
198+
// +kubebuilder:object:root=true
199+
// +kubebuilder:resource:shortName=ocirepo
200+
// +kubebuilder:subresource:status
201+
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
202+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
203+
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
204+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
205+
206+
// OCIRepository is the Schema for the ocirepositories API
207+
type OCIRepository struct {
208+
metav1.TypeMeta `json:",inline"`
209+
metav1.ObjectMeta `json:"metadata,omitempty"`
210+
211+
Spec OCIRepositorySpec `json:"spec,omitempty"`
212+
// +kubebuilder:default={"observedGeneration":-1}
213+
Status OCIRepositoryStatus `json:"status,omitempty"`
214+
}
215+
216+
// OCIRepositoryList contains a list of OCIRepository
217+
// +kubebuilder:object:root=true
218+
type OCIRepositoryList struct {
219+
metav1.TypeMeta `json:",inline"`
220+
metav1.ListMeta `json:"metadata,omitempty"`
221+
Items []OCIRepository `json:"items"`
222+
}
223+
224+
func init() {
225+
SchemeBuilder.Register(&OCIRepository{}, &OCIRepositoryList{})
226+
}

0 commit comments

Comments
 (0)