controllers: make OCIRepository compat with RFC-0005

Signed-off-by: Hidde Beydals <hello@hidde.co>
This commit is contained in:
Hidde Beydals 2023-01-16 21:41:35 +00:00
parent 909ece4092
commit 469c9387ee
2 changed files with 90 additions and 64 deletions

View File

@ -22,6 +22,7 @@ import (
"crypto/x509" "crypto/x509"
"errors" "errors"
"fmt" "fmt"
"github.com/fluxcd/pkg/git"
"io" "io"
"net/http" "net/http"
"os" "os"
@ -390,7 +391,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
return sreconcile.ResultEmpty, e return sreconcile.ResultEmpty, e
} }
// Get the upstream revision from the artifact digest // Get the upstream revision from the artifact revision
revision, err := r.getRevision(url, opts.craneOpts) revision, err := r.getRevision(url, opts.craneOpts)
if err != nil { if err != nil {
e := serror.NewGeneric( e := serror.NewGeneric(
@ -405,7 +406,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
// Mark observations about the revision on the object // Mark observations about the revision on the object
defer func() { defer func() {
if !obj.GetArtifact().HasRevision(revision) { if obj.GetArtifact() == nil || git.TransformRevision(obj.GetArtifact().Revision) != git.TransformRevision(revision) {
message := fmt.Sprintf("new revision '%s' for '%s'", revision, url) message := fmt.Sprintf("new revision '%s' for '%s'", revision, url)
if obj.GetArtifact() != nil { if obj.GetArtifact() != nil {
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", message) conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", message)
@ -425,7 +426,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
if obj.Spec.Verify == nil { if obj.Spec.Verify == nil {
// Remove old observations if verification was disabled // Remove old observations if verification was disabled
conditions.Delete(obj, sourcev1.SourceVerifiedCondition) conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
} else if !obj.GetArtifact().HasRevision(revision) || } else if (obj.GetArtifact() == nil || git.TransformRevision(obj.GetArtifact().Revision) != git.TransformRevision(revision)) ||
conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation || conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation ||
conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) { conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
@ -458,7 +459,9 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
// Skip pulling if the artifact revision and the source configuration has // Skip pulling if the artifact revision and the source configuration has
// not changed. // not changed.
if obj.GetArtifact().HasRevision(revision) && !ociContentConfigChanged(obj) { if (obj.GetArtifact() != nil &&
git.TransformRevision(obj.GetArtifact().Revision) == git.TransformRevision(revision)) &&
!ociContentConfigChanged(obj) {
conditions.Delete(obj, sourcev1.FetchFailedCondition) conditions.Delete(obj, sourcev1.FetchFailedCondition)
return sreconcile.ResultSuccess, nil return sreconcile.ResultSuccess, nil
} }
@ -582,7 +585,7 @@ func (r *OCIRepositoryReconciler) selectLayer(obj *sourcev1.OCIRepository, image
return blob, nil return blob, nil
} }
// getRevision fetches the upstream digest and returns the revision in the format `<tag>/<digest>` // getRevision fetches the upstream revision and returns the revision in the format `<tag>/<revision>`
func (r *OCIRepositoryReconciler) getRevision(url string, options []crane.Option) (string, error) { func (r *OCIRepositoryReconciler) getRevision(url string, options []crane.Option) (string, error) {
ref, err := name.ParseReference(url) ref, err := name.ParseReference(url)
if err != nil { if err != nil {
@ -609,16 +612,16 @@ func (r *OCIRepositoryReconciler) getRevision(url string, options []crane.Option
return "", err return "", err
} }
revision := digestHash.Hex revision := digestHash.String()
if repoTag != "" { if repoTag != "" {
revision = fmt.Sprintf("%s/%s", repoTag, digestHash.Hex) revision = fmt.Sprintf("%s@%s", repoTag, revision)
} }
return revision, nil return revision, nil
} }
// digestFromRevision extract the digest from the revision string // digestFromRevision extract the revision from the revision string
func (r *OCIRepositoryReconciler) digestFromRevision(revision string) string { func (r *OCIRepositoryReconciler) digestFromRevision(revision string) string {
parts := strings.Split(revision, "/") parts := strings.Split(revision, "@")
return parts[len(parts)-1] return parts[len(parts)-1]
} }
@ -722,7 +725,7 @@ func (r *OCIRepositoryReconciler) parseRepositoryURL(obj *sourcev1.OCIRepository
return ref.Context().Name(), nil return ref.Context().Name(), nil
} }
// getArtifactURL determines which tag or digest should be used and returns the OCI artifact FQN. // getArtifactURL determines which tag or revision should be used and returns the OCI artifact FQN.
func (r *OCIRepositoryReconciler) getArtifactURL(obj *sourcev1.OCIRepository, options []crane.Option) (string, error) { func (r *OCIRepositoryReconciler) getArtifactURL(obj *sourcev1.OCIRepository, options []crane.Option) (string, error) {
url, err := r.parseRepositoryURL(obj) url, err := r.parseRepositoryURL(obj)
if err != nil { if err != nil {
@ -967,7 +970,9 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, sp *pat
}() }()
// The artifact is up-to-date // The artifact is up-to-date
if obj.GetArtifact().HasRevision(artifact.Revision) && !ociContentConfigChanged(obj) { if (obj.GetArtifact() != nil &&
git.TransformRevision(obj.GetArtifact().Revision) == git.TransformRevision(revision)) &&
!ociContentConfigChanged(obj) {
r.eventLogf(ctx, obj, eventv1.EventTypeTrace, sourcev1.ArtifactUpToDateReason, r.eventLogf(ctx, obj, eventv1.EventTypeTrace, sourcev1.ArtifactUpToDateReason,
"artifact up-to-date with remote revision: '%s'", artifact.Revision) "artifact up-to-date with remote revision: '%s'", artifact.Revision)
return sreconcile.ResultSuccess, nil return sreconcile.ResultSuccess, nil
@ -1141,7 +1146,7 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *so
fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaChecksumKey): newObj.Status.Artifact.Checksum, fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaChecksumKey): newObj.Status.Artifact.Checksum,
} }
if newObj.Status.Artifact.Digest != "" { if newObj.Status.Artifact.Digest != "" {
annotations[sourcev1.GroupVersion.Group+"/digest"] = newObj.Status.Artifact.Digest annotations[sourcev1.GroupVersion.Group+"/revision"] = newObj.Status.Artifact.Digest
} }
var oldChecksum string var oldChecksum string

View File

@ -86,7 +86,7 @@ func TestOCIRepository_Reconcile(t *testing.T) {
url string url string
tag string tag string
semver string semver string
digest string revision string
mediaType string mediaType string
operation string operation string
assertArtifact []artifactFixture assertArtifact []artifactFixture
@ -95,7 +95,7 @@ func TestOCIRepository_Reconcile(t *testing.T) {
name: "public tag", name: "public tag",
url: podinfoVersions["6.1.6"].url, url: podinfoVersions["6.1.6"].url,
tag: podinfoVersions["6.1.6"].tag, tag: podinfoVersions["6.1.6"].tag,
digest: fmt.Sprintf("%s/%s", podinfoVersions["6.1.6"].tag, podinfoVersions["6.1.6"].digest.Hex), revision: fmt.Sprintf("%s@%s", podinfoVersions["6.1.6"].tag, podinfoVersions["6.1.6"].digest.String()),
mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
operation: sourcev1.OCILayerCopy, operation: sourcev1.OCILayerCopy,
assertArtifact: []artifactFixture{ assertArtifact: []artifactFixture{
@ -110,10 +110,10 @@ func TestOCIRepository_Reconcile(t *testing.T) {
}, },
}, },
{ {
name: "public semver", name: "public semver",
url: podinfoVersions["6.1.5"].url, url: podinfoVersions["6.1.5"].url,
semver: ">= 6.1 <= 6.1.5", semver: ">= 6.1 <= 6.1.5",
digest: fmt.Sprintf("%s/%s", podinfoVersions["6.1.5"].tag, podinfoVersions["6.1.5"].digest.Hex), revision: fmt.Sprintf("%s@%s", podinfoVersions["6.1.5"].tag, podinfoVersions["6.1.5"].digest.String()),
assertArtifact: []artifactFixture{ assertArtifact: []artifactFixture{
{ {
expectedPath: "kustomize/deployment.yaml", expectedPath: "kustomize/deployment.yaml",
@ -177,8 +177,8 @@ func TestOCIRepository_Reconcile(t *testing.T) {
// Wait for the object to be Ready // Wait for the object to be Ready
waitForSourceReadyWithArtifact(ctx, g, obj) waitForSourceReadyWithArtifact(ctx, g, obj)
// Check if the revision matches the expected digest // Check if the revision matches the expected revision
g.Expect(obj.Status.Artifact.Revision).To(Equal(tt.digest)) g.Expect(obj.Status.Artifact.Revision).To(Equal(tt.revision))
// Check if the metadata matches the expected annotations // Check if the metadata matches the expected annotations
g.Expect(obj.Status.Artifact.Metadata[oci.SourceAnnotation]).To(ContainSubstring("podinfo")) g.Expect(obj.Status.Artifact.Metadata[oci.SourceAnnotation]).To(ContainSubstring("podinfo"))
@ -293,7 +293,6 @@ func TestOCIRepository_Reconcile_MediaType(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
ns, err := testEnv.CreateNamespace(ctx, "ocirepository-mediatype-test") ns, err := testEnv.CreateNamespace(ctx, "ocirepository-mediatype-test")
@ -383,8 +382,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
name: "HTTP without basic auth", name: "HTTP without basic auth",
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
}, },
}, },
{ {
@ -404,8 +403,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
includeSecret: true, includeSecret: true,
}, },
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
}, },
}, },
{ {
@ -425,8 +424,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
includeSA: true, includeSA: true,
}, },
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
}, },
}, },
{ {
@ -508,8 +507,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
}, },
}, },
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
}, },
}, },
{ {
@ -580,8 +579,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
}, },
provider: "azure", provider: "azure",
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
}, },
}, },
} }
@ -678,7 +677,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
assertConditions := tt.assertConditions assertConditions := tt.assertConditions
for k := range assertConditions { for k := range assertConditions {
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<digest>", fmt.Sprintf("%s/%s", img.tag, img.digest.Hex)) assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<revision>", fmt.Sprintf("%s@%s", img.tag, img.digest.String()))
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<url>", repoURL) assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<url>", repoURL)
} }
@ -750,7 +749,7 @@ func TestOCIRepository_CertSecret(t *testing.T) {
digest: pi.digest, digest: pi.digest,
certSecret: &tlsSecretClientCert, certSecret: &tlsSecretClientCert,
expectreadyconition: true, expectreadyconition: true,
expectedstatusmessage: fmt.Sprintf("stored artifact for digest '%s'", pi.digest.Hex), expectedstatusmessage: fmt.Sprintf("stored artifact for digest '%s'", pi.digest.String()),
}, },
{ {
name: "test connection with no secret", name: "test connection with no secret",
@ -874,7 +873,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
{ {
name: "no reference (latest tag)", name: "no reference (latest tag)",
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
wantRevision: fmt.Sprintf("latest/%s", img6.digest.Hex), wantRevision: fmt.Sprintf("latest@%s", img6.digest.String()),
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -886,7 +885,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
Tag: "6.1.6", Tag: "6.1.6",
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
wantRevision: fmt.Sprintf("%s/%s", img6.tag, img6.digest.Hex), wantRevision: fmt.Sprintf("%s@%s", img6.tag, img6.digest.String()),
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -898,7 +897,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
SemVer: ">= 6.1.5", SemVer: ">= 6.1.5",
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
wantRevision: fmt.Sprintf("%s/%s", img6.tag, img6.digest.Hex), wantRevision: fmt.Sprintf("%s@%s", img6.tag, img6.digest.String()),
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -909,7 +908,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Digest: img6.digest.String(), Digest: img6.digest.String(),
}, },
wantRevision: img6.digest.Hex, wantRevision: img6.digest.String(),
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -956,7 +955,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
Tag: "6.1.5", Tag: "6.1.5",
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
wantRevision: fmt.Sprintf("%s/%s", img6.tag, img6.digest.Hex), wantRevision: fmt.Sprintf("%s@%s", img6.tag, img6.digest.String()),
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -970,7 +969,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
Digest: img5.digest.String(), Digest: img5.digest.String(),
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
wantRevision: img5.digest.Hex, wantRevision: img5.digest.String(),
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision"),
@ -1058,13 +1057,13 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Tag: "6.1.4", Tag: "6.1.4",
}, },
digest: img4.digest.Hex, digest: img4.digest.String(),
shouldSign: true, shouldSign: true,
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision <digest>"), *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision <revision>"),
}, },
}, },
{ {
@ -1072,13 +1071,13 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Tag: "6.1.5", Tag: "6.1.5",
}, },
digest: img5.digest.Hex, digest: img5.digest.String(),
wantErr: true, wantErr: true,
wantErrMsg: "failed to verify the signature using provider 'cosign': no matching signatures were found for '<url>'", wantErrMsg: "failed to verify the signature using provider 'cosign': no matching signatures were found for '<url>'",
want: sreconcile.ResultEmpty, want: sreconcile.ResultEmpty,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider>': no matching signatures were found for '<url>'"), *conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider>': no matching signatures were found for '<url>'"),
}, },
}, },
@ -1087,34 +1086,34 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Tag: "6.1.5", Tag: "6.1.5",
}, },
digest: img5.digest.Hex, digest: img5.digest.String(),
wantErr: true, wantErr: true,
want: sreconcile.ResultEmpty, want: sreconcile.ResultEmpty,
keyless: true, keyless: true,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider> keyless': no matching signatures"), *conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider> keyless': no matching signatures"),
}, },
}, },
{ {
name: "verify failed before, removed from spec, remove condition", name: "verify failed before, removed from spec, remove condition",
reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"}, reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"},
digest: img4.digest.Hex, digest: img4.digest.String(),
beforeFunc: func(obj *sourcev1.OCIRepository) { beforeFunc: func(obj *sourcev1.OCIRepository) {
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, "VerifyFailed", "fail msg") conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, "VerifyFailed", "fail msg")
obj.Spec.Verify = nil obj.Spec.Verify = nil
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s/%s", img4.tag, img4.digest.Hex)} obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
}, },
{ {
name: "same artifact, verified before, change in obj gen verify again", name: "same artifact, verified before, change in obj gen verify again",
reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"}, reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"},
digest: img4.digest.Hex, digest: img4.digest.String(),
shouldSign: true, shouldSign: true,
beforeFunc: func(obj *sourcev1.OCIRepository) { beforeFunc: func(obj *sourcev1.OCIRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s/%s", img4.tag, img4.digest.Hex)} obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
// Set Verified with old observed generation and different reason/message. // Set Verified with old observed generation and different reason/message.
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified") conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified")
// Set new object generation. // Set new object generation.
@ -1122,17 +1121,17 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision <digest>"), *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision <revision>"),
}, },
}, },
{ {
name: "no verify for already verified, verified condition remains the same", name: "no verify for already verified, verified condition remains the same",
reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"}, reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.4"},
digest: img4.digest.Hex, digest: img4.digest.String(),
shouldSign: true, shouldSign: true,
beforeFunc: func(obj *sourcev1.OCIRepository) { beforeFunc: func(obj *sourcev1.OCIRepository) {
// Artifact present and custom verified condition reason/message. // Artifact present and custom verified condition reason/message.
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s/%s", img4.tag, img4.digest.Hex)} obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified") conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified")
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -1145,14 +1144,14 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Tag: "6.1.4", Tag: "6.1.4",
}, },
digest: img4.digest.Hex, digest: img4.digest.String(),
shouldSign: true, shouldSign: true,
insecure: true, insecure: true,
wantErr: true, wantErr: true,
want: sreconcile.ResultEmpty, want: sreconcile.ResultEmpty,
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<digest>' for '<url>'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "cosign does not support insecure registries"), *conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "cosign does not support insecure registries"),
}, },
}, },
@ -1248,7 +1247,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
assertConditions := tt.assertConditions assertConditions := tt.assertConditions
for k := range assertConditions { for k := range assertConditions {
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<digest>", fmt.Sprintf("%s/%s", tt.reference.Tag, tt.digest)) assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<revision>", fmt.Sprintf("%s@%s", tt.reference.Tag, tt.digest))
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<url>", artifactURL) assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<url>", artifactURL)
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<provider>", "cosign") assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<provider>", "cosign")
} }
@ -1282,7 +1281,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
func TestOCIRepository_reconcileSource_noop(t *testing.T) { func TestOCIRepository_reconcileSource_noop(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
testRevision := "6.1.5/d1fc4595915714af2492dc4b66097de1e10f80150c8899907d8f8e61c6d6f67d" testRevision := "6.1.5@sha256:8a0eed109e056ab1f7e70e8fb47e00cf6f560ca5cd910c83451882e07edb77fa"
tmpDir := t.TempDir() tmpDir := t.TempDir()
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{}) server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
@ -1316,6 +1315,28 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) {
g.Expect(artifact.Metadata).To(BeEmpty()) g.Expect(artifact.Metadata).To(BeEmpty())
}, },
}, },
{
name: "noop - artifact revisions match (legacy)",
beforeFunc: func(obj *sourcev1.OCIRepository) {
obj.Status.Artifact = &sourcev1.Artifact{
Revision: "6.1.5/8a0eed109e056ab1f7e70e8fb47e00cf6f560ca5cd910c83451882e07edb77fa",
}
},
afterFunc: func(g *WithT, artifact *sourcev1.Artifact) {
g.Expect(artifact.Metadata).To(BeEmpty())
},
},
{
name: "noop - artifact revisions match (legacy: digest)",
beforeFunc: func(obj *sourcev1.OCIRepository) {
obj.Status.Artifact = &sourcev1.Artifact{
Revision: "8a0eed109e056ab1f7e70e8fb47e00cf6f560ca5cd910c83451882e07edb77fa",
}
},
afterFunc: func(g *WithT, artifact *sourcev1.Artifact) {
g.Expect(artifact.Metadata).To(BeEmpty())
},
},
{ {
name: "full reconcile - same rev, unobserved ignore", name: "full reconcile - same rev, unobserved ignore",
beforeFunc: func(obj *sourcev1.OCIRepository) { beforeFunc: func(obj *sourcev1.OCIRepository) {
@ -1723,9 +1744,9 @@ func TestOCIRepository_getArtifactURL(t *testing.T) {
name: "valid url with digest reference", name: "valid url with digest reference",
url: "oci://ghcr.io/stefanprodan/charts", url: "oci://ghcr.io/stefanprodan/charts",
reference: &sourcev1.OCIRepositoryRef{ reference: &sourcev1.OCIRepositoryRef{
Digest: imgs["6.1.6"].digest.Hex, Digest: imgs["6.1.6"].digest.String(),
}, },
want: "ghcr.io/stefanprodan/charts@" + imgs["6.1.6"].digest.Hex, want: "ghcr.io/stefanprodan/charts@" + imgs["6.1.6"].digest.String(),
}, },
{ {
name: "valid url with semver reference", name: "valid url with semver reference",
@ -2236,7 +2257,7 @@ func pushMultiplePodinfoImages(serverURL string, versions ...string) (map[string
func setPodinfoImageAnnotations(img gcrv1.Image, tag string) gcrv1.Image { func setPodinfoImageAnnotations(img gcrv1.Image, tag string) gcrv1.Image {
metadata := map[string]string{ metadata := map[string]string{
oci.SourceAnnotation: "https://github.com/stefanprodan/podinfo", oci.SourceAnnotation: "https://github.com/stefanprodan/podinfo",
oci.RevisionAnnotation: fmt.Sprintf("%s/SHA", tag), oci.RevisionAnnotation: fmt.Sprintf("%s@sha256:8a0eed109e056ab1f7e70e8fb47e00cf6f560ca5cd910c83451882e07edb77fa", tag),
} }
return mutate.Annotations(img, metadata).(gcrv1.Image) return mutate.Annotations(img, metadata).(gcrv1.Image)
} }