@@ -50,6 +50,8 @@ import (
5050 "sigs.k8s.io/controller-runtime/pkg/ratelimiter"
5151
5252 "github.com/fluxcd/pkg/apis/meta"
53+ "github.com/fluxcd/pkg/oci"
54+ "github.com/fluxcd/pkg/oci/auth/login"
5355 "github.com/fluxcd/pkg/runtime/conditions"
5456 helper "github.com/fluxcd/pkg/runtime/controller"
5557 "github.com/fluxcd/pkg/runtime/events"
@@ -64,14 +66,6 @@ import (
6466 "github.com/fluxcd/source-controller/internal/util"
6567)
6668
67- const (
68- ClientCert = "certFile"
69- ClientKey = "keyFile"
70- CACert = "caFile"
71- OCISourceKey = "org.opencontainers.image.source"
72- OCIRevisionKey = "org.opencontainers.image.revision"
73- )
74-
7569// ociRepositoryReadyCondition contains the information required to summarize a
7670// v1beta2.OCIRepository Ready Condition.
7771var ociRepositoryReadyCondition = summarize.Conditions {
@@ -297,15 +291,33 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
297291 ctxTimeout , cancel := context .WithTimeout (ctx , obj .Spec .Timeout .Duration )
298292 defer cancel ()
299293
300- // Generate the registry credential keychain
301- keychain , err := r .keychain (ctx , obj )
302- if err != nil {
303- e := serror .NewGeneric (
304- fmt .Errorf ("failed to get credential: %w" , err ),
305- sourcev1 .AuthenticationFailedReason ,
306- )
307- conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
308- return sreconcile .ResultEmpty , e
294+ options := r .craneOptions (ctxTimeout )
295+
296+ // Generate the registry credential keychain either from static credentials or using cloud OIDC
297+ if obj .Spec .Provider == sourcev1 .GenericOCIProvider {
298+ keychain , err := r .keychain (ctx , obj )
299+ if err != nil {
300+ e := serror .NewGeneric (
301+ fmt .Errorf ("failed to get credential: %w" , err ),
302+ sourcev1 .AuthenticationFailedReason ,
303+ )
304+ conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
305+ return sreconcile .ResultEmpty , e
306+ }
307+ options = append (options , crane .WithAuthFromKeychain (keychain ))
308+ } else {
309+ auth , authErr := r .oidcAuth (ctxTimeout , obj )
310+ if authErr != nil && ! errors .Is (authErr , oci .ErrUnconfiguredProvider ) {
311+ e := serror .NewGeneric (
312+ fmt .Errorf ("failed to get credential from %s: %w" , obj .Spec .Provider , authErr ),
313+ sourcev1 .AuthenticationFailedReason ,
314+ )
315+ conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
316+ return sreconcile .ResultEmpty , e
317+ }
318+ if auth != nil {
319+ options = append (options , crane .WithAuth (auth ))
320+ }
309321 }
310322
311323 // Generate the transport for remote operations
@@ -318,9 +330,12 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
318330 conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
319331 return sreconcile .ResultEmpty , e
320332 }
333+ if transport != nil {
334+ options = append (options , crane .WithTransport (transport ))
335+ }
321336
322337 // Determine which artifact revision to pull
323- url , err := r .getArtifactURL (ctxTimeout , obj , keychain , transport )
338+ url , err := r .getArtifactURL (obj , options )
324339 if err != nil {
325340 e := serror .NewGeneric (
326341 fmt .Errorf ("failed to determine the artifact address for '%s': %w" , obj .Spec .URL , err ),
@@ -330,7 +345,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
330345 }
331346
332347 // Pull artifact from the remote container registry
333- img , err := crane .Pull (url , r . craneOptions ( ctxTimeout , keychain , transport ) ... )
348+ img , err := crane .Pull (url , options ... )
334349 if err != nil {
335350 e := serror .NewGeneric (
336351 fmt .Errorf ("failed to pull artifact from '%s': %w" , obj .Spec .URL , err ),
@@ -441,8 +456,7 @@ func (r *OCIRepositoryReconciler) parseRepositoryURL(obj *sourcev1.OCIRepository
441456}
442457
443458// getArtifactURL determines which tag or digest should be used and returns the OCI artifact FQN.
444- func (r * OCIRepositoryReconciler ) getArtifactURL (ctx context.Context ,
445- obj * sourcev1.OCIRepository , keychain authn.Keychain , transport http.RoundTripper ) (string , error ) {
459+ func (r * OCIRepositoryReconciler ) getArtifactURL (obj * sourcev1.OCIRepository , options []crane.Option ) (string , error ) {
446460 url , err := r .parseRepositoryURL (obj )
447461 if err != nil {
448462 return "" , err
@@ -454,7 +468,7 @@ func (r *OCIRepositoryReconciler) getArtifactURL(ctx context.Context,
454468 }
455469
456470 if obj .Spec .Reference .SemVer != "" {
457- tag , err := r .getTagBySemver (ctx , url , obj .Spec .Reference .SemVer , keychain , transport )
471+ tag , err := r .getTagBySemver (url , obj .Spec .Reference .SemVer , options )
458472 if err != nil {
459473 return "" , err
460474 }
@@ -471,9 +485,8 @@ func (r *OCIRepositoryReconciler) getArtifactURL(ctx context.Context,
471485
472486// getTagBySemver call the remote container registry, fetches all the tags from the repository,
473487// and returns the latest tag according to the semver expression.
474- func (r * OCIRepositoryReconciler ) getTagBySemver (ctx context.Context ,
475- url , exp string , keychain authn.Keychain , transport http.RoundTripper ) (string , error ) {
476- tags , err := crane .ListTags (url , r .craneOptions (ctx , keychain , transport )... )
488+ func (r * OCIRepositoryReconciler ) getTagBySemver (url , exp string , options []crane.Option ) (string , error ) {
489+ tags , err := crane .ListTags (url , options ... )
477490 if err != nil {
478491 return "" , err
479492 }
@@ -567,20 +580,20 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *sourcev1.O
567580 transport := remote .DefaultTransport .Clone ()
568581 tlsConfig := transport .TLSClientConfig
569582
570- if clientCert , ok := certSecret .Data [ClientCert ]; ok {
583+ if clientCert , ok := certSecret .Data [oci . ClientCert ]; ok {
571584 // parse and set client cert and secret
572- if clientKey , ok := certSecret .Data [ClientKey ]; ok {
585+ if clientKey , ok := certSecret .Data [oci . ClientKey ]; ok {
573586 cert , err := tls .X509KeyPair (clientCert , clientKey )
574587 if err != nil {
575588 return nil , err
576589 }
577590 tlsConfig .Certificates = append (tlsConfig .Certificates , cert )
578591 } else {
579- return nil , fmt .Errorf ("'%s' found in secret, but no %s" , ClientCert , ClientKey )
592+ return nil , fmt .Errorf ("'%s' found in secret, but no %s" , oci . ClientCert , oci . ClientKey )
580593 }
581594 }
582595
583- if caCert , ok := certSecret .Data [CACert ]; ok {
596+ if caCert , ok := certSecret .Data [oci . CACert ]; ok {
584597 syscerts , err := x509 .SystemCertPool ()
585598 if err != nil {
586599 return nil , err
@@ -592,20 +605,34 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *sourcev1.O
592605
593606}
594607
608+ // oidcAuth generates the OIDC credential authenticator based on the specified cloud provider.
609+ func (r * OCIRepositoryReconciler ) oidcAuth (ctx context.Context , obj * sourcev1.OCIRepository ) (authn.Authenticator , error ) {
610+ url := strings .TrimPrefix (obj .Spec .URL , sourcev1 .OCIRepositoryPrefix )
611+ ref , err := name .ParseReference (url )
612+ if err != nil {
613+ return nil , fmt .Errorf ("failed to parse URL '%s': %w" , obj .Spec .URL , err )
614+ }
615+
616+ opts := login.ProviderOptions {}
617+ switch obj .Spec .Provider {
618+ case sourcev1 .AmazonOCIProvider :
619+ opts .AwsAutoLogin = true
620+ case sourcev1 .AzureOCIProvider :
621+ opts .AzureAutoLogin = true
622+ case sourcev1 .GoogleOCIProvider :
623+ opts .GcpAutoLogin = true
624+ }
625+
626+ return login .NewManager ().Login (ctx , url , ref , opts )
627+ }
628+
595629// craneOptions sets the auth headers, timeout and user agent
596630// for all operations against remote container registries.
597- func (r * OCIRepositoryReconciler ) craneOptions (ctx context.Context ,
598- keychain authn.Keychain , transport http.RoundTripper ) []crane.Option {
631+ func (r * OCIRepositoryReconciler ) craneOptions (ctx context.Context ) []crane.Option {
599632 options := []crane.Option {
600633 crane .WithContext (ctx ),
601- crane .WithUserAgent ("flux/v2" ),
602- crane .WithAuthFromKeychain (keychain ),
634+ crane .WithUserAgent (oci .UserAgent ),
603635 }
604-
605- if transport != nil {
606- options = append (options , crane .WithTransport (transport ))
607- }
608-
609636 return options
610637}
611638
@@ -834,10 +861,10 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context,
834861 // enrich message with upstream annotations if found
835862 if info := newObj .GetArtifact ().Metadata ; info != nil {
836863 var source , revision string
837- if val , ok := info [OCISourceKey ]; ok {
864+ if val , ok := info [oci . SourceAnnotation ]; ok {
838865 source = val
839866 }
840- if val , ok := info [OCIRevisionKey ]; ok {
867+ if val , ok := info [oci . RevisionAnnotation ]; ok {
841868 revision = val
842869 }
843870 if source != "" && revision != "" {
0 commit comments