Skip to content

Commit 17be047

Browse files
Add OCI source for provider ConfigMap preparation
Signed-off-by: Danil-Grigorev <danil.grigorev@suse.com>
1 parent 7d39b00 commit 17be047

File tree

7 files changed

+102
-8
lines changed

7 files changed

+102
-8
lines changed

api/v1alpha2/provider_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ type FetchConfiguration struct {
218218
// +optional
219219
URL string `json:"url,omitempty"`
220220

221+
// OCI to be used for fetching the provider’s components and metadata from an OCI artifact
222+
// You must set `providerSpec.Version` field for operator to pick up the deesired version of the image.
223+
// +optional
224+
OCI *OCI `json:"oci,omitempty"`
225+
221226
// Selector to be used for fetching provider’s components and metadata from
222227
// ConfigMaps stored inside the cluster. Each ConfigMap is expected to contain
223228
// components and metadata for a specific version only.
@@ -227,6 +232,14 @@ type FetchConfiguration struct {
227232
Selector *metav1.LabelSelector `json:"selector,omitempty"`
228233
}
229234

235+
type OCI struct {
236+
// URL to be used for fetching the provider’s components and metadata from an OCI artifact.
237+
// You must set `providerSpec.Version` field for operator to pick up desired version of the release from GitHub.
238+
// If the providerSpec.Version is missing, latest provider version from registry is used.
239+
// +optional
240+
URL string `json:"url,omitempty"`
241+
}
242+
230243
// ProviderStatus defines the observed state of the Provider.
231244
type ProviderStatus struct {
232245
// Contract will contain the core provider contract that the provider is

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ require (
2323
k8s.io/component-base v0.31.4
2424
k8s.io/klog/v2 v2.130.1
2525
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
26+
oras.land/oras-go/v2 v2.5.0
2627
sigs.k8s.io/cluster-api v1.9.0
2728
sigs.k8s.io/controller-runtime v0.19.3
2829
sigs.k8s.io/yaml v1.4.0
@@ -79,6 +80,7 @@ require (
7980
github.com/modern-go/reflect2 v1.0.2 // indirect
8081
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
8182
github.com/opencontainers/go-digest v1.0.0 // indirect
83+
github.com/opencontainers/image-spec v1.1.0 // indirect
8284
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
8385
github.com/pkg/errors v0.9.1 // indirect
8486
github.com/prometheus/client_golang v1.19.1 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
174174
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
175175
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
176176
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
177+
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
178+
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
177179
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
178180
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
179181
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -407,6 +409,8 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F
407409
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
408410
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
409411
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
412+
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
413+
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
410414
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY=
411415
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
412416
sigs.k8s.io/cluster-api v1.9.0 h1:Iud4Zj8R/t7QX5Rvs9/V+R8HDLbf7QPVemrWfZi4g54=

internal/controller/manifests_downloader.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
configMapVersionLabel = "provider.cluster.x-k8s.io/version"
4040
configMapTypeLabel = "provider.cluster.x-k8s.io/type"
4141
configMapNameLabel = "provider.cluster.x-k8s.io/name"
42+
configMapSourceLabel = "provider.cluster.x-k8s.io/source"
4243
operatorManagedLabel = "managed-by.operator.cluster.x-k8s.io"
4344

4445
compressedAnnotation = "provider.cluster.x-k8s.io/compressed"
@@ -48,6 +49,7 @@ const (
4849
additionalManifestsConfigMapKey = "manifests"
4950

5051
maxConfigMapSize = 1 * 1024 * 1024
52+
ociSource = "oci"
5153
)
5254

5355
// downloadManifests downloads CAPI manifests from a url.
@@ -96,6 +98,12 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
9698
p.provider.SetSpec(spec)
9799
}
98100

101+
if p.provider.GetSpec().FetchConfig.OCI != nil {
102+
err := p.fetchOCI(ctx)
103+
104+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
105+
}
106+
99107
// Fetch the provider metadata and components yaml files from the provided repository GitHub/GitLab.
100108
metadataFile, err := repo.GetFile(ctx, spec.Version, metadataFile)
101109
if err != nil {
@@ -113,7 +121,7 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
113121

114122
withCompression := needToCompress(metadataFile, componentsFile)
115123

116-
if err := p.createManifestsConfigMap(ctx, metadataFile, componentsFile, withCompression); err != nil {
124+
if err := p.createManifestsConfigMap(ctx, p.prepareConfigMapLabels(), metadataFile, componentsFile, withCompression); err != nil {
117125
err = fmt.Errorf("failed to create config map for provider %q: %w", p.provider.GetName(), err)
118126

119127
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
@@ -149,14 +157,14 @@ func (p *phaseReconciler) prepareConfigMapLabels() map[string]string {
149157
}
150158

151159
// createManifestsConfigMap creates a config map with downloaded manifests.
152-
func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, metadata, components []byte, compress bool) error {
160+
func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, labels map[string]string, metadata, components []byte, compress bool) error {
153161
configMapName := fmt.Sprintf("%s-%s-%s", p.provider.GetType(), p.provider.GetName(), p.provider.GetSpec().Version)
154162

155163
configMap := &corev1.ConfigMap{
156164
ObjectMeta: metav1.ObjectMeta{
157165
Name: configMapName,
158166
Namespace: p.provider.GetNamespace(),
159-
Labels: p.prepareConfigMapLabels(),
167+
Labels: labels,
160168
},
161169
Data: map[string]string{
162170
metadataConfigMapKey: string(metadata),
@@ -211,12 +219,18 @@ func providerLabelSelector(provider operatorv1.GenericProvider) *metav1.LabelSel
211219
return provider.GetSpec().FetchConfig.Selector
212220
}
213221

222+
if provider.GetSpec().FetchConfig != nil && provider.GetSpec().FetchConfig.OCI != nil {
223+
return &metav1.LabelSelector{
224+
MatchLabels: ociLabels(provider),
225+
}
226+
}
227+
214228
return &metav1.LabelSelector{
215229
MatchLabels: providerLabels(provider),
216230
}
217231
}
218232

219-
// prepareConfigMapLabels returns default set of labels that identify a config map with downloaded manifests.
233+
// providerLabels returns default set of labels that identify a config map with downloaded manifests.
220234
func providerLabels(provider operatorv1.GenericProvider) map[string]string {
221235
return map[string]string{
222236
configMapVersionLabel: provider.GetSpec().Version,
@@ -226,6 +240,17 @@ func providerLabels(provider operatorv1.GenericProvider) map[string]string {
226240
}
227241
}
228242

243+
// ociLabels returns default set of labels that identify a config map created from OCI artifacts.
244+
func ociLabels(provider operatorv1.GenericProvider) map[string]string {
245+
return map[string]string{
246+
configMapVersionLabel: provider.GetSpec().Version,
247+
configMapTypeLabel: provider.GetType(),
248+
configMapNameLabel: provider.GetName(),
249+
configMapSourceLabel: ociSource,
250+
operatorManagedLabel: "true",
251+
}
252+
}
253+
229254
// needToCompress checks whether the input data exceeds the maximum configmap
230255
// size limit and returns whether it should be compressed.
231256
func needToCompress(bs ...[]byte) bool {

internal/controller/phases.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"io"
2525
"os"
26+
"path"
2627

2728
corev1 "k8s.io/api/core/v1"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -33,6 +34,9 @@ import (
3334
"k8s.io/apimachinery/pkg/util/wait"
3435
"k8s.io/client-go/kubernetes/scheme"
3536
"k8s.io/client-go/rest"
37+
"oras.land/oras-go/v2"
38+
"oras.land/oras-go/v2/content/file"
39+
"oras.land/oras-go/v2/registry/remote"
3640
operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
3741
"sigs.k8s.io/cluster-api-operator/internal/controller/genericprovider"
3842
"sigs.k8s.io/cluster-api-operator/util"
@@ -49,7 +53,8 @@ import (
4953
)
5054

5155
const (
52-
metadataFile = "metadata.yaml"
56+
metadataFile = "metadata.yaml"
57+
componentsFile = "components.yaml"
5358
)
5459

5560
// phaseReconciler holds all required information for interacting with clusterctl code and
@@ -279,6 +284,51 @@ func (p *phaseReconciler) secretReader(ctx context.Context, providers ...configc
279284
return mr, nil
280285
}
281286

287+
func (p *phaseReconciler) fetchOCI(ctx context.Context) error {
288+
log := ctrl.LoggerFrom(ctx)
289+
290+
log.Info("Custom fetch configuration OCI url was provided")
291+
292+
rep, err := remote.NewRepository(p.provider.GetSpec().FetchConfig.OCI.URL)
293+
if err != nil {
294+
log.Error(err, "Invalid registry URL specified")
295+
296+
return err
297+
}
298+
299+
fs, err := file.New(os.TempDir())
300+
if err != nil {
301+
log.Error(err, "Unable to open FS repository")
302+
303+
return err
304+
}
305+
306+
spec := p.provider.GetSpec()
307+
308+
_, err = oras.Copy(context.Background(), rep, spec.Version, fs, spec.Version, oras.DefaultCopyOptions)
309+
if err != nil {
310+
log.Error(err, "Unable to copy OCI content to filesystem")
311+
312+
return err
313+
}
314+
315+
metadata, err := os.ReadFile(path.Join(os.TempDir(), metadataFile))
316+
if err != nil {
317+
log.Error(err, "Metadata file read error")
318+
319+
return err
320+
}
321+
322+
components, err := os.ReadFile(path.Join(os.TempDir(), componentsFile))
323+
if err != nil {
324+
log.Error(err, "Components file read error")
325+
326+
return err
327+
}
328+
329+
return p.createManifestsConfigMap(ctx, ociLabels(p.provider), metadata, components, true)
330+
}
331+
282332
// configmapRepository use clusterctl NewMemoryRepository structure to store the manifests
283333
// and metadata from a given configmap.
284334
func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector *metav1.LabelSelector, namespace, additionalManifests string) (repository.Repository, error) {

test/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ require (
8888
github.com/modern-go/reflect2 v1.0.2 // indirect
8989
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
9090
github.com/opencontainers/go-digest v1.0.0 // indirect
91-
github.com/opencontainers/image-spec v1.0.2 // indirect
91+
github.com/opencontainers/image-spec v1.1.0 // indirect
9292
github.com/pelletier/go-toml v1.9.5 // indirect
9393
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
9494
github.com/pkg/errors v0.9.1 // indirect

test/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
180180
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
181181
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
182182
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
183-
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
184-
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
183+
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
184+
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
185185
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
186186
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
187187
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=

0 commit comments

Comments
 (0)