@@ -50,6 +50,8 @@ import (
50
50
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
51
51
52
52
"github.com/fluxcd/pkg/apis/meta"
53
+ "github.com/fluxcd/pkg/oci"
54
+ "github.com/fluxcd/pkg/oci/auth/login"
53
55
"github.com/fluxcd/pkg/runtime/conditions"
54
56
helper "github.com/fluxcd/pkg/runtime/controller"
55
57
"github.com/fluxcd/pkg/runtime/events"
@@ -64,14 +66,6 @@ import (
64
66
"github.com/fluxcd/source-controller/internal/util"
65
67
)
66
68
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
-
75
69
// ociRepositoryReadyCondition contains the information required to summarize a
76
70
// v1beta2.OCIRepository Ready Condition.
77
71
var ociRepositoryReadyCondition = summarize.Conditions {
@@ -297,7 +291,9 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
297
291
ctxTimeout , cancel := context .WithTimeout (ctx , obj .Spec .Timeout .Duration )
298
292
defer cancel ()
299
293
300
- // Generate the registry credential keychain
294
+ options := r .craneOptions (ctxTimeout )
295
+
296
+ // Generate the registry credential keychain either from static credentials or using cloud OIDC
301
297
keychain , err := r .keychain (ctx , obj )
302
298
if err != nil {
303
299
e := serror .NewGeneric (
@@ -307,6 +303,22 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
307
303
conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
308
304
return sreconcile .ResultEmpty , e
309
305
}
306
+ options = append (options , crane .WithAuthFromKeychain (keychain ))
307
+
308
+ if obj .Spec .Provider != sourcev1 .GenericOCIProvider {
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
+ }
321
+ }
310
322
311
323
// Generate the transport for remote operations
312
324
transport , err := r .transport (ctx , obj )
@@ -318,9 +330,12 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
318
330
conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , e .Err .Error ())
319
331
return sreconcile .ResultEmpty , e
320
332
}
333
+ if transport != nil {
334
+ options = append (options , crane .WithTransport (transport ))
335
+ }
321
336
322
337
// Determine which artifact revision to pull
323
- url , err := r .getArtifactURL (ctxTimeout , obj , keychain , transport )
338
+ url , err := r .getArtifactURL (obj , options )
324
339
if err != nil {
325
340
e := serror .NewGeneric (
326
341
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
330
345
}
331
346
332
347
// 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 ... )
334
349
if err != nil {
335
350
e := serror .NewGeneric (
336
351
fmt .Errorf ("failed to pull artifact from '%s': %w" , obj .Spec .URL , err ),
@@ -441,8 +456,7 @@ func (r *OCIRepositoryReconciler) parseRepositoryURL(obj *sourcev1.OCIRepository
441
456
}
442
457
443
458
// 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 ) {
446
460
url , err := r .parseRepositoryURL (obj )
447
461
if err != nil {
448
462
return "" , err
@@ -454,7 +468,7 @@ func (r *OCIRepositoryReconciler) getArtifactURL(ctx context.Context,
454
468
}
455
469
456
470
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 )
458
472
if err != nil {
459
473
return "" , err
460
474
}
@@ -471,9 +485,8 @@ func (r *OCIRepositoryReconciler) getArtifactURL(ctx context.Context,
471
485
472
486
// getTagBySemver call the remote container registry, fetches all the tags from the repository,
473
487
// 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 ... )
477
490
if err != nil {
478
491
return "" , err
479
492
}
@@ -567,20 +580,20 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *sourcev1.O
567
580
transport := remote .DefaultTransport .Clone ()
568
581
tlsConfig := transport .TLSClientConfig
569
582
570
- if clientCert , ok := certSecret .Data [ClientCert ]; ok {
583
+ if clientCert , ok := certSecret .Data [oci . ClientCert ]; ok {
571
584
// parse and set client cert and secret
572
- if clientKey , ok := certSecret .Data [ClientKey ]; ok {
585
+ if clientKey , ok := certSecret .Data [oci . ClientKey ]; ok {
573
586
cert , err := tls .X509KeyPair (clientCert , clientKey )
574
587
if err != nil {
575
588
return nil , err
576
589
}
577
590
tlsConfig .Certificates = append (tlsConfig .Certificates , cert )
578
591
} 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 )
580
593
}
581
594
}
582
595
583
- if caCert , ok := certSecret .Data [CACert ]; ok {
596
+ if caCert , ok := certSecret .Data [oci . CACert ]; ok {
584
597
syscerts , err := x509 .SystemCertPool ()
585
598
if err != nil {
586
599
return nil , err
@@ -592,20 +605,34 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *sourcev1.O
592
605
593
606
}
594
607
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
+
595
629
// craneOptions sets the auth headers, timeout and user agent
596
630
// 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 {
599
632
options := []crane.Option {
600
633
crane .WithContext (ctx ),
601
- crane .WithUserAgent ("flux/v2" ),
602
- crane .WithAuthFromKeychain (keychain ),
634
+ crane .WithUserAgent (oci .UserAgent ),
603
635
}
604
-
605
- if transport != nil {
606
- options = append (options , crane .WithTransport (transport ))
607
- }
608
-
609
636
return options
610
637
}
611
638
@@ -834,10 +861,10 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context,
834
861
// enrich message with upstream annotations if found
835
862
if info := newObj .GetArtifact ().Metadata ; info != nil {
836
863
var source , revision string
837
- if val , ok := info [OCISourceKey ]; ok {
864
+ if val , ok := info [oci . SourceAnnotation ]; ok {
838
865
source = val
839
866
}
840
- if val , ok := info [OCIRevisionKey ]; ok {
867
+ if val , ok := info [oci . RevisionAnnotation ]; ok {
841
868
revision = val
842
869
}
843
870
if source != "" && revision != "" {
0 commit comments