@@ -39,6 +39,7 @@ import (
39
39
corev1 "k8s.io/api/core/v1"
40
40
"k8s.io/apimachinery/pkg/runtime"
41
41
"k8s.io/apimachinery/pkg/types"
42
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
42
43
"k8s.io/apimachinery/pkg/util/sets"
43
44
kuberecorder "k8s.io/client-go/tools/record"
44
45
"k8s.io/utils/ptr"
@@ -392,7 +393,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
392
393
393
394
// Get the upstream revision from the artifact digest
394
395
// TODO: getRevision resolves the digest, which may change before image is fetched, so it should probaly update ref
395
- revision , err := r .getRevision (ref , opts )
396
+ revision , ref , desc , err := r .getRevision (ref , opts )
396
397
if err != nil {
397
398
e := serror .NewGeneric (
398
399
fmt .Errorf ("failed to determine artifact digest: %w" , err ),
@@ -454,7 +455,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
454
455
return sreconcile .ResultSuccess , nil
455
456
}
456
457
457
- blob , serr := r .fetchArtifact (obj , metadata , ref , opts )
458
+ blob , serr := r .fetchArtifact (obj , metadata , ref , desc , opts )
458
459
if serr != nil {
459
460
conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , serr .Reason , serr .Err .Error ())
460
461
return sreconcile .ResultEmpty , serr
@@ -558,63 +559,130 @@ func (r *OCIRepositoryReconciler) selectLayer(obj *ociv1.OCIRepository, image gc
558
559
return blob , nil
559
560
}
560
561
561
- func (r * OCIRepositoryReconciler ) fetchArtifact (obj * ociv1.OCIRepository , metadata * sourcev1.Artifact , ref name.Reference , options remoteOptions ) (io.ReadCloser , * serror.Generic ) {
562
- // Pull artifact from the remote container registry
563
- img , err := remote .Image (ref , options ... )
564
- if err != nil {
565
- e := serror .NewGeneric (
566
- fmt .Errorf ("failed to pull artifact from '%s': %w" , obj .Spec .URL , err ),
567
- ociv1 .OCIPullFailedReason ,
568
- )
569
- return nil , e
570
- }
562
+ func newPullErr (format string , a ... any ) * serror.Generic {
563
+ return serror .NewGeneric (fmt .Errorf (format , a ... ), ociv1 .OCIPullFailedReason )
564
+ }
571
565
572
- // Copy the OCI annotations to the internal artifact metadata
573
- manifest , err := img .Manifest ()
574
- if err != nil {
575
- e := serror .NewGeneric (
576
- fmt .Errorf ("failed to parse artifact manifest: %w" , err ),
577
- ociv1 .OCILayerOperationFailedReason ,
578
- )
579
- return nil , e
566
+ func newLayerOperationErr (format string , a ... any ) * serror.Generic {
567
+ return serror .NewGeneric (fmt .Errorf (format , a ... ), ociv1 .OCILayerOperationFailedReason )
568
+ }
569
+
570
+ func (r * OCIRepositoryReconciler ) fetchArtifact (obj * ociv1.OCIRepository , metadata * sourcev1.Artifact , ref name.Reference , desc * remote.Descriptor , options remoteOptions ) (io.ReadCloser , * serror.Generic ) {
571
+ switch mt := desc .MediaType ; {
572
+ case mt .IsImage ():
573
+ // Pull artifact from the remote container registry
574
+ img , err := desc .Image ()
575
+ if err != nil {
576
+ return nil , newPullErr ("failed to parse artifact image from '%s': %w" , obj .Spec .URL , err )
577
+ }
578
+
579
+ // Copy the OCI annotations to the internal artifact metadata
580
+ manifest , err := img .Manifest ()
581
+ if err != nil {
582
+ return nil , newLayerOperationErr ("failed to parse artifact image manifest: %w" , err )
583
+ }
584
+ metadata .Metadata = manifest .Annotations
585
+
586
+ // Extract the compressed content from the selected layer
587
+ blob , err := r .selectLayer (obj , img )
588
+ if err != nil {
589
+ e := serror .NewGeneric (err , ociv1 .OCILayerOperationFailedReason )
590
+ return nil , e
591
+ }
592
+ return blob , nil
593
+ case mt .IsIndex ():
594
+ idx , err := desc .ImageIndex ()
595
+ if err != nil {
596
+ return nil , newPullErr ("failed to parse artifact index from '%s': %w" , obj .Spec .URL , err )
597
+ }
598
+
599
+ manifest , err := idx .IndexManifest ()
600
+ if err != nil {
601
+ return nil , newPullErr ("failed to parse artifact index manifest: %w" , err )
602
+ }
603
+
604
+ if len (manifest .Manifests ) == 0 {
605
+ return nil , newLayerOperationErr ("empty index" )
606
+ }
607
+
608
+ images := make ([]gcrv1.Image , 0 , len (manifest .Manifests ))
609
+
610
+ for i := range manifest .Manifests {
611
+ manifest := manifest .Manifests [i ]
612
+ if manifest .MediaType .IsIndex () {
613
+ r .Eventf (obj , corev1 .EventTypeWarning , "OCINestedIndexUnsupported" , "skipping nested index manifest '%s' in '%s'" , manifest .Digest .String (), desc .Digest .String ())
614
+ continue
615
+ }
616
+ if ! manifest .MediaType .IsImage () {
617
+ r .Eventf (obj , corev1 .EventTypeNormal , "OCIImageUnsupported" , "skipping runnable image '%s' in '%s'" , manifest .Digest .String (), ref )
618
+ continue
619
+ }
620
+ if manifest .ArtifactType != "" {
621
+ img , err := idx .Image (manifest .Digest )
622
+ if err != nil {
623
+ return nil , newPullErr ("failed to pull artifact image '%s' from '%s': %w" , manifest .Digest .String (), ref , err )
624
+ }
625
+ images = append (images , img )
626
+ }
627
+ }
628
+
629
+ if len (images ) == 0 {
630
+ return nil , newPullErr ("no suitable artifacts found in index '%s': %w" , desc .Digest .String (), err )
631
+ }
632
+
633
+ var errs []error
634
+ for i := range images {
635
+ blob , err := r .selectLayer (obj , images [i ])
636
+ if err != nil {
637
+ errs = append (errs , err )
638
+ continue
639
+ }
640
+ return blob , nil
641
+ }
642
+ if len (errs ) > 0 {
643
+ return nil , newLayerOperationErr ("%w" , kerrors .NewAggregate (errs ))
644
+ }
645
+ return nil , newPullErr ("no suitable layers found in index '%s': %w" , desc .Digest .String (), err )
646
+ default :
647
+ return nil , newLayerOperationErr ("media type '%s' of '%s' is not index or image" , mt , ref )
580
648
}
581
- metadata . Metadata = manifest . Annotations
649
+ }
582
650
583
- // Extract the compressed content from the selected layer
584
- blob , err := r .selectLayer (obj , img )
651
+ func (r * OCIRepositoryReconciler ) getDescriptor (ref name.Reference , options remoteOptions ) (* remote.Descriptor , error ) {
652
+ // NB: there is no good enought reason to use remote.Head first,
653
+ // as it's only in error case that remote.Get won't have to be
654
+ // done afterwards anyway
655
+ desc , err := remote .Get (ref , options ... )
585
656
if err != nil {
586
- e := serror .NewGeneric (err , ociv1 .OCILayerOperationFailedReason )
587
- return nil , e
657
+ return nil , fmt .Errorf ("failed to fetch %w" , err )
588
658
}
589
- return blob , nil
659
+ return desc , nil
660
+
590
661
}
591
662
592
663
// getRevision fetches the upstream digest, returning the revision in the
593
664
// format '<tag>@<digest>'.
594
- func (r * OCIRepositoryReconciler ) getRevision (ref name.Reference , options []remote. Option ) (string , error ) {
665
+ func (r * OCIRepositoryReconciler ) getRevision (ref name.Reference , options remoteOptions ) (string , name. Reference , * remote. Descriptor , error ) {
595
666
switch ref := ref .(type ) {
596
667
case name.Digest :
597
668
digest , err := gcrv1 .NewHash (ref .DigestStr ())
598
669
if err != nil {
599
- return "" , err
670
+ return "" , nil , nil , err
600
671
}
601
- return digest .String (), nil
672
+ desc , err := r .getDescriptor (ref , options )
673
+ if err != nil {
674
+ return "" , nil , nil , fmt .Errorf ("unable to check digest in registry: %w" , err )
675
+ }
676
+ return digest .String (), ref , desc , nil
602
677
case name.Tag :
603
- var digest gcrv1.Hash
604
-
605
- desc , err := remote .Head (ref , options ... )
606
- if err == nil {
607
- digest = desc .Digest
608
- } else {
609
- rdesc , err := remote .Get (ref , options ... )
610
- if err != nil {
611
- return "" , err
612
- }
613
- digest = rdesc .Descriptor .Digest
678
+ desc , err := r .getDescriptor (ref , options )
679
+ if err != nil {
680
+ return "" , nil , nil , err
614
681
}
615
- return fmt .Sprintf ("%s@%s" , ref .TagStr (), digest .String ()), nil
682
+ digest := desc .Digest .String ()
683
+ return fmt .Sprintf ("%s@%s" , ref .TagStr (), digest ), ref .Digest (digest ), desc , nil
616
684
default :
617
- return "" , fmt .Errorf ("unsupported reference type: %T" , ref )
685
+ return "" , nil , nil , fmt .Errorf ("unsupported reference type: %T" , ref )
618
686
}
619
687
}
620
688
0 commit comments