Skip to content

Commit 4a4089d

Browse files
committed
Add support for using an OCI image as source
This change defines an OCIImage CRD that allows a user to specify a given image to use as source. The contents of the image (including images with multiple layers) are converted into a TAR and exposed to consumers following the same conventions as the other source types. apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: OCIImage metadata: name: ociimage-sample spec: image: index.docker.io/stefanprodan/podinfo:latest interval: 1m In addition to the image, interval, timeout, ignore, and suspend keys (all of which behave consistently with the existing source types) this CRD also defines both imagePullSecrets and serviceAccountName keys which provide ways to contribute registry connection credentials for the specified image. This change also adds a new way to write to the storage archive by streaming data from an incoming TAR without writing it to the filesystem. A couple of code and test functions were extracted to reuse common functionality for both archive strategies. Signed-off-by: Ben Hale <[email protected]>
1 parent d9f19a8 commit 4a4089d

18 files changed

+1947
-119
lines changed

.github/workflows/e2e.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
kubectl -n source-system wait gitrepository/gitrepository-sample --for=condition=ready --timeout=1m
5757
kubectl -n source-system wait helmrepository/helmrepository-sample --for=condition=ready --timeout=1m
5858
kubectl -n source-system wait helmchart/helmchart-sample --for=condition=ready --timeout=1m
59+
kubectl -n source-system wait ociimage/ociimage-sample --for=condition=ready --timeout=1m
5960
kubectl -n source-system delete -f ./config/samples
6061
- name: Run HelmChart values file tests
6162
run: |
@@ -113,6 +114,7 @@ jobs:
113114
kubectl -n source-system get gitrepositories -oyaml
114115
kubectl -n source-system get helmrepositories -oyaml
115116
kubectl -n source-system get helmcharts -oyaml
117+
kubectl -n source-system get ociimages -oyaml
116118
kubectl -n source-system get all
117119
kubectl -n source-system logs deploy/source-controller
118120
kubectl -n minio get all

PROJECT

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ resources:
1313
- group: source
1414
kind: Bucket
1515
version: v1beta1
16+
- group: source
17+
kind: OCIImage
18+
version: v1beta1
1619
version: "2"

api/v1beta1/ociimage_types.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
Copyright 2020 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 v1beta1
18+
19+
import (
20+
"github.com/fluxcd/pkg/apis/meta"
21+
apimeta "k8s.io/apimachinery/pkg/api/meta"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
)
24+
25+
const (
26+
// OCIImageKind is the string representation of a OCIImage.
27+
OCIImageKind = "OCIImage"
28+
)
29+
30+
// OCIImageSpec defines the desired state of OCIImage
31+
type OCIImageSpec struct {
32+
33+
// Image is a reference to an image in a remote registry
34+
// +required
35+
Image string `json:"image"`
36+
37+
// ImagePullSecrets contains the names of the Kubernetes Secrets containing registry login
38+
// information to resolve image metadata.
39+
// +optional
40+
ImagePullSecrets []meta.LocalObjectReference `json:"imagePullSecrets,omitempty"`
41+
42+
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
43+
// the image pull if the service account has attached pull secrets. For more information:
44+
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account
45+
// +optional
46+
ServiceAccountName string `json:"serviceAccountName,omitempty"`
47+
48+
// The interval at which to check for image updates.
49+
// +required
50+
Interval metav1.Duration `json:"interval"`
51+
52+
// The timeout for remote OCI Image operations like pulling, defaults to 20s.
53+
// +kubebuilder:default="20s"
54+
// +optional
55+
Timeout *metav1.Duration `json:"timeout,omitempty"`
56+
57+
// Ignore overrides the set of excluded patterns in the .sourceignore format
58+
// (which is the same as .gitignore). If not provided, a default will be used,
59+
// consult the documentation for your version to find out what those are.
60+
// +optional
61+
Ignore *string `json:"ignore,omitempty"`
62+
63+
// This flag tells the controller to suspend the reconciliation of this source.
64+
// +optional
65+
Suspend bool `json:"suspend,omitempty"`
66+
}
67+
68+
// OCIImageStatus defines the observed state of OCIImage
69+
type OCIImageStatus struct {
70+
// ObservedGeneration is the last observed generation.
71+
// +optional
72+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
73+
74+
// Conditions holds the conditions for the OCIImage.
75+
// +optional
76+
Conditions []metav1.Condition `json:"conditions,omitempty"`
77+
78+
// URL is the download link for the artifact output of the last image
79+
// sync.
80+
// +optional
81+
URL string `json:"url,omitempty"`
82+
83+
// Artifact represents the output of the last successful image sync.
84+
// +optional
85+
Artifact *Artifact `json:"artifact,omitempty"`
86+
87+
meta.ReconcileRequestStatus `json:",inline"`
88+
}
89+
90+
const (
91+
// OCIImageOperationSucceedReason represents the fact that the image pull operation succeeded.
92+
OCIImageOperationSucceedReason string = "OCIImageOperationSucceed"
93+
94+
// OCIImageOperationFailedReason represents the fact that the image pull operation failed.
95+
OCIImageOperationFailedReason string = "OCIImageOperationFailed"
96+
)
97+
98+
// OCIImageProgressing resets the conditions of the OCIImage to
99+
// metav1.Condition of type meta.ReadyCondition with status 'Unknown' and
100+
// meta.ProgressingReason reason and message. It returns the modified
101+
// OCCIImage.
102+
func OCIImageProgressing(image OCIImage) OCIImage {
103+
image.Status.ObservedGeneration = image.Generation
104+
image.Status.URL = ""
105+
image.Status.Conditions = []metav1.Condition{}
106+
meta.SetResourceCondition(&image, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress")
107+
return image
108+
}
109+
110+
// OCIImageReady sets the given Artifact and URL on the OCIImage and
111+
// sets the meta.ReadyCondition to 'True', with the given reason and message. It
112+
// returns the modified OCIImage.
113+
func OCIImageReady(image OCIImage, artifact Artifact, url, reason, message string) OCIImage {
114+
image.Status.Artifact = &artifact
115+
image.Status.URL = url
116+
meta.SetResourceCondition(&image, meta.ReadyCondition, metav1.ConditionTrue, reason, message)
117+
return image
118+
}
119+
120+
// OCIImageNotReady sets the meta.ReadyCondition on the given OCIImage
121+
// to 'False', with the given reason and message. It returns the modified
122+
// OCIImage.
123+
func OCIImageNotReady(image OCIImage, reason, message string) OCIImage {
124+
meta.SetResourceCondition(&image, meta.ReadyCondition, metav1.ConditionFalse, reason, message)
125+
return image
126+
}
127+
128+
// OCIImageReadyMessage returns the message of the metav1.Condition of type
129+
// meta.ReadyCondition with status 'True' if present, or an empty string.
130+
func OCIImageReadyMessage(image OCIImage) string {
131+
if c := apimeta.FindStatusCondition(image.Status.Conditions, meta.ReadyCondition); c != nil {
132+
if c.Status == metav1.ConditionTrue {
133+
return c.Message
134+
}
135+
}
136+
return ""
137+
}
138+
139+
// GetArtifact returns the latest artifact from the source if present in the
140+
// status sub-resource.
141+
func (in *OCIImage) GetArtifact() *Artifact {
142+
return in.Status.Artifact
143+
}
144+
145+
// GetImagePullSecretNames returns the names of all the configured image pull secrets.
146+
func (in *OCIImage) GetImagePullSecretNames() []string {
147+
var n []string
148+
149+
for _, i := range in.Spec.ImagePullSecrets {
150+
n = append(n, i.Name)
151+
}
152+
153+
return n
154+
}
155+
156+
// GetStatusConditions returns a pointer to the Status.Conditions slice
157+
func (in *OCIImage) GetStatusConditions() *[]metav1.Condition {
158+
return &in.Status.Conditions
159+
}
160+
161+
// GetInterval returns the interval at which the source is updated.
162+
func (in *OCIImage) GetInterval() metav1.Duration {
163+
return in.Spec.Interval
164+
}
165+
166+
// +genclient
167+
// +genclient:Namespaced
168+
//+kubebuilder:object:root=true
169+
//+kubebuilder:subresource:status
170+
//+kubebuilder:printcolumn:name="Image",type=string,JSONPath=`.spec.image`
171+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
172+
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
173+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
174+
175+
// OCIImage is the Schema for the ociimages API
176+
type OCIImage struct {
177+
metav1.TypeMeta `json:",inline"`
178+
metav1.ObjectMeta `json:"metadata,omitempty"`
179+
180+
Spec OCIImageSpec `json:"spec,omitempty"`
181+
Status OCIImageStatus `json:"status,omitempty"`
182+
}
183+
184+
//+kubebuilder:object:root=true
185+
186+
// OCIImageList contains a list of OCIImage
187+
type OCIImageList struct {
188+
metav1.TypeMeta `json:",inline"`
189+
metav1.ListMeta `json:"metadata,omitempty"`
190+
Items []OCIImage `json:"items"`
191+
}
192+
193+
func init() {
194+
SchemeBuilder.Register(&OCIImage{}, &OCIImageList{})
195+
}

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)