Skip to content

Commit 28def20

Browse files
committed
Add oci:// prefix
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
1 parent 47e5a90 commit 28def20

File tree

4 files changed

+40
-13
lines changed

4 files changed

+40
-13
lines changed

api/v1beta2/ocirepository_types.go

+4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ import (
2525
const (
2626
// OCIRepositoryKind is the string representation of a OCIRepository.
2727
OCIRepositoryKind = "OCIRepository"
28+
29+
// OCIRepositoryPrefix is the prefix used for OCIRepository URLs.
30+
OCIRepositoryPrefix = "oci://"
2831
)
2932

3033
// OCIRepositorySpec defines the desired state of OCIRepository
3134
type OCIRepositorySpec struct {
3235
// URL is a reference to an OCI artifact repository hosted
3336
// on a remote container registry.
37+
// +kubebuilder:validation:Pattern="^oci://"
3438
// +required
3539
URL string `json:"url"`
3640

config/samples/source_v1beta2_ocirepository.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ metadata:
44
name: ocirepository-sample
55
spec:
66
interval: 1m
7-
url: ghcr.io/stefanprodan/manifests/podinfo
7+
url: oci://ghcr.io/stefanprodan/manifests/podinfo
88
ref:
99
tag: 6.1.6

controllers/ocirepository_controller.go

+25-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"github.com/google/go-containerregistry/pkg/name"
2324
"os"
2425
"sort"
26+
"strings"
2527
"time"
2628

2729
"github.com/Masterminds/semver/v3"
@@ -363,24 +365,43 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
363365
return sreconcile.ResultSuccess, nil
364366
}
365367

368+
// parseRepositoryURL extracts the repository URL.
369+
func (r *OCIRepositoryReconciler) parseRepositoryURL(obj *sourcev1.OCIRepository) (string, error) {
370+
if !strings.HasPrefix(obj.Spec.URL, sourcev1.OCIRepositoryPrefix) {
371+
return "", fmt.Errorf("URL must be in format 'oci://<domain>/<org>/<repo>'")
372+
}
373+
374+
url := strings.TrimPrefix(obj.Spec.URL, sourcev1.OCIRepositoryPrefix)
375+
ref, err := name.ParseReference(url)
376+
if err != nil {
377+
return "", fmt.Errorf("'%s' invalid URL: %w", obj.Spec.URL, err)
378+
}
379+
380+
return ref.Context().Name(), nil
381+
}
382+
366383
// getArtifactURL determines which tag or digest should be used and returns the OCI artifact FQN.
367384
func (r *OCIRepositoryReconciler) getArtifactURL(ctx context.Context, obj *sourcev1.OCIRepository, keychain authn.Keychain) (string, error) {
368-
url := obj.Spec.URL
385+
url, err := r.parseRepositoryURL(obj)
386+
if err != nil {
387+
return "", err
388+
}
389+
369390
if obj.Spec.Reference != nil {
370391
if obj.Spec.Reference.Digest != "" {
371-
return fmt.Sprintf("%s@%s", obj.Spec.URL, obj.Spec.Reference.Digest), nil
392+
return fmt.Sprintf("%s@%s", url, obj.Spec.Reference.Digest), nil
372393
}
373394

374395
if obj.Spec.Reference.SemVer != "" {
375396
tag, err := r.getTagBySemver(ctx, url, obj.Spec.Reference.SemVer, keychain)
376397
if err != nil {
377398
return "", err
378399
}
379-
return fmt.Sprintf("%s:%s", obj.Spec.URL, tag), nil
400+
return fmt.Sprintf("%s:%s", url, tag), nil
380401
}
381402

382403
if obj.Spec.Reference.Tag != "" {
383-
return fmt.Sprintf("%s:%s", obj.Spec.URL, obj.Spec.Reference.Tag), nil
404+
return fmt.Sprintf("%s:%s", url, obj.Spec.Reference.Tag), nil
384405
}
385406
}
386407

controllers/ocirepository_controller_test.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ func TestOCIRepository_SecretRef(t *testing.T) {
239239
g.Expect(err).ToNot(HaveOccurred())
240240

241241
repositoryURL := fmt.Sprintf("%s/podinfo", regServer.registryHost)
242+
ociURL := fmt.Sprintf("oci://%s", repositoryURL)
242243

243244
// Push Test Image
244245
err = crane.Push(image, repositoryURL, crane.WithAuth(&authn.Basic{
@@ -260,14 +261,14 @@ func TestOCIRepository_SecretRef(t *testing.T) {
260261
}{
261262
{
262263
name: "private-registry-access-via-secretref",
263-
url: repositoryURL,
264+
url: ociURL,
264265
digest: podinfoImageDigest,
265266
includeSecretRef: true,
266267
includeServiceAccount: false,
267268
},
268269
{
269270
name: "private-registry-access-via-serviceaccount",
270-
url: repositoryURL,
271+
url: ociURL,
271272
digest: podinfoImageDigest,
272273
includeSecretRef: false,
273274
includeServiceAccount: true,
@@ -289,7 +290,7 @@ func TestOCIRepository_SecretRef(t *testing.T) {
289290
},
290291
Type: corev1.SecretTypeDockerConfigJson,
291292
StringData: map[string]string{
292-
".dockerconfigjson": fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, tt.url, testRegistryUsername, testRegistryPassword),
293+
".dockerconfigjson": fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, repositoryURL, testRegistryUsername, testRegistryPassword),
293294
},
294295
}
295296
g.Expect(testEnv.CreateAndWait(ctx, secret)).To(Succeed())
@@ -435,6 +436,7 @@ func TestOCIRepository_FailedAuth(t *testing.T) {
435436
g.Expect(err).ToNot(HaveOccurred())
436437

437438
repositoryURL := fmt.Sprintf("%s/podinfo", regServer.registryHost)
439+
ociURL := fmt.Sprintf("oci://%s", repositoryURL)
438440

439441
// Push Test Image
440442
err = crane.Push(image, repositoryURL, crane.WithAuth(&authn.Basic{
@@ -458,7 +460,7 @@ func TestOCIRepository_FailedAuth(t *testing.T) {
458460
}{
459461
{
460462
name: "missing-auth",
461-
url: repositoryURL,
463+
url: ociURL,
462464
repoUsername: "",
463465
repoPassword: "",
464466
digest: podinfoImageDigest,
@@ -467,7 +469,7 @@ func TestOCIRepository_FailedAuth(t *testing.T) {
467469
},
468470
{
469471
name: "invalid-auth-via-secret",
470-
url: repositoryURL,
472+
url: ociURL,
471473
repoUsername: "InvalidUser",
472474
repoPassword: "InvalidPassword",
473475
digest: podinfoImageDigest,
@@ -476,7 +478,7 @@ func TestOCIRepository_FailedAuth(t *testing.T) {
476478
},
477479
{
478480
name: "invalid-auth-via-service-account",
479-
url: repositoryURL,
481+
url: ociURL,
480482
repoUsername: "InvalidUser",
481483
repoPassword: "InvalidPassword",
482484
digest: podinfoImageDigest,
@@ -500,7 +502,7 @@ func TestOCIRepository_FailedAuth(t *testing.T) {
500502
},
501503
Type: corev1.SecretTypeDockerConfigJson,
502504
StringData: map[string]string{
503-
".dockerconfigjson": fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, tt.url, tt.repoUsername, tt.repoPassword),
505+
".dockerconfigjson": fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, repositoryURL, tt.repoUsername, tt.repoPassword),
504506
},
505507
}
506508
g.Expect(testEnv.CreateAndWait(ctx, secret)).To(Succeed())
@@ -623,7 +625,7 @@ func createPodinfoImageFromTar(tarFileName, tag string, imageServer *httptest.Se
623625
}
624626

625627
return &podinfoImage{
626-
url: repositoryURL,
628+
url: "oci://" + repositoryURL,
627629
tag: tag,
628630
digest: podinfoImageDigest,
629631
}, nil

0 commit comments

Comments
 (0)