@@ -35,6 +35,7 @@ import (
35
35
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36
36
"k8s.io/apimachinery/pkg/runtime"
37
37
"k8s.io/apimachinery/pkg/types"
38
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
38
39
"k8s.io/apimachinery/pkg/util/uuid"
39
40
kuberecorder "k8s.io/client-go/tools/record"
40
41
ctrl "sigs.k8s.io/controller-runtime"
@@ -460,9 +461,10 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
460
461
loginOpts []helmreg.LoginOption
461
462
)
462
463
464
+ normalizedURL := strings .TrimSuffix (repo .Spec .URL , "/" )
463
465
// Construct the Getter options from the HelmRepository data
464
466
clientOpts := []helmgetter.Option {
465
- helmgetter .WithURL (repo . Spec . URL ),
467
+ helmgetter .WithURL (normalizedURL ),
466
468
helmgetter .WithTimeout (repo .Spec .Timeout .Duration ),
467
469
helmgetter .WithPassCredentialsAll (repo .Spec .PassCredentials ),
468
470
}
@@ -490,7 +492,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
490
492
}
491
493
clientOpts = append (clientOpts , opts ... )
492
494
493
- tlsConfig , err = getter .TLSClientConfigFromSecret (* secret , repo . Spec . URL )
495
+ tlsConfig , err = getter .TLSClientConfigFromSecret (* secret , normalizedURL )
494
496
if err != nil {
495
497
e := & serror.Event {
496
498
Err : fmt .Errorf ("failed to create TLS client config with secret data: %w" , err ),
@@ -502,7 +504,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
502
504
}
503
505
504
506
// Build registryClient options from secret
505
- loginOpt , err := registry .LoginOptionFromSecret (repo . Spec . URL , * secret )
507
+ loginOpt , err := registry .LoginOptionFromSecret (normalizedURL , * secret )
506
508
if err != nil {
507
509
e := & serror.Event {
508
510
Err : fmt .Errorf ("failed to configure Helm client with secret data: %w" , err ),
@@ -517,11 +519,11 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
517
519
}
518
520
519
521
// Initialize the chart repository
520
- var chartRepo chart.Repository
522
+ var chartRepo chart.Downloader
521
523
switch repo .Spec .Type {
522
524
case sourcev1 .HelmRepositoryTypeOCI :
523
- if ! helmreg .IsOCI (repo . Spec . URL ) {
524
- err := fmt .Errorf ("invalid OCI registry URL: %s" , repo . Spec . URL )
525
+ if ! helmreg .IsOCI (normalizedURL ) {
526
+ err := fmt .Errorf ("invalid OCI registry URL: %s" , normalizedURL )
525
527
return chartRepoConfigErrorReturn (err , obj )
526
528
}
527
529
@@ -550,7 +552,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
550
552
551
553
// Tell the chart repository to use the OCI client with the configured getter
552
554
clientOpts = append (clientOpts , helmgetter .WithRegistryClient (registryClient ))
553
- ociChartRepo , err := repository .NewOCIChartRepository (repo . Spec . URL , repository .WithOCIGetter (r .Getters ), repository .WithOCIGetterOptions (clientOpts ), repository .WithOCIRegistryClient (registryClient ))
555
+ ociChartRepo , err := repository .NewOCIChartRepository (normalizedURL , repository .WithOCIGetter (r .Getters ), repository .WithOCIGetterOptions (clientOpts ), repository .WithOCIRegistryClient (registryClient ))
554
556
if err != nil {
555
557
return chartRepoConfigErrorReturn (err , obj )
556
558
}
@@ -570,7 +572,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
570
572
}
571
573
}
572
574
default :
573
- httpChartRepo , err := repository .NewChartRepository (repo . Spec . URL , r .Storage .LocalPath (* repo .GetArtifact ()), r .Getters , tlsConfig , clientOpts ,
575
+ httpChartRepo , err := repository .NewChartRepository (normalizedURL , r .Storage .LocalPath (* repo .GetArtifact ()), r .Getters , tlsConfig , clientOpts ,
574
576
repository .WithMemoryCache (r .Storage .LocalPath (* repo .GetArtifact ()), r .Cache , r .TTL , func (event string ) {
575
577
r .IncCacheEvents (event , obj .Name , obj .Namespace )
576
578
}))
@@ -683,7 +685,7 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
683
685
684
686
// Setup dependency manager
685
687
dm := chart .NewDependencyManager (
686
- chart .WithRepositoryCallback (r .namespacedChartRepositoryCallback (ctx , obj .GetName (), obj .GetNamespace ())),
688
+ chart .WithDownloaderCallback (r .namespacedChartRepositoryCallback (ctx , obj .GetName (), obj .GetNamespace ())),
687
689
)
688
690
defer dm .Clear ()
689
691
@@ -912,12 +914,17 @@ func (r *HelmChartReconciler) garbageCollect(ctx context.Context, obj *sourcev1.
912
914
return nil
913
915
}
914
916
915
- // namespacedChartRepositoryCallback returns a chart.GetChartRepositoryCallback scoped to the given namespace.
917
+ // namespacedChartRepositoryCallback returns a chart.GetChartDownloaderCallback scoped to the given namespace.
916
918
// The returned callback returns a repository.ChartRepository configured with the retrieved v1beta1.HelmRepository,
917
919
// or a shim with defaults if no object could be found.
918
- func (r * HelmChartReconciler ) namespacedChartRepositoryCallback (ctx context.Context , name , namespace string ) chart.GetChartRepositoryCallback {
919
- return func (url string ) (* repository.ChartRepository , error ) {
920
- var tlsConfig * tls.Config
920
+ // The callback returns an object with a state, so the caller has to do the necessary cleanup.
921
+ func (r * HelmChartReconciler ) namespacedChartRepositoryCallback (ctx context.Context , name , namespace string ) chart.GetChartDownloaderCallback {
922
+ return func (url string ) (chart.CleanDownloader , error ) {
923
+ var (
924
+ tlsConfig * tls.Config
925
+ loginOpts []helmreg.LoginOption
926
+ )
927
+ normalizedURL := strings .TrimSuffix (url , "/" )
921
928
repo , err := r .resolveDependencyRepository (ctx , url , namespace )
922
929
if err != nil {
923
930
// Return Kubernetes client errors, but ignore others
@@ -932,7 +939,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
932
939
}
933
940
}
934
941
clientOpts := []helmgetter.Option {
935
- helmgetter .WithURL (repo . Spec . URL ),
942
+ helmgetter .WithURL (normalizedURL ),
936
943
helmgetter .WithTimeout (repo .Spec .Timeout .Duration ),
937
944
helmgetter .WithPassCredentialsAll (repo .Spec .PassCredentials ),
938
945
}
@@ -946,26 +953,72 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
946
953
}
947
954
clientOpts = append (clientOpts , opts ... )
948
955
949
- tlsConfig , err = getter .TLSClientConfigFromSecret (* secret , repo . Spec . URL )
956
+ tlsConfig , err = getter .TLSClientConfigFromSecret (* secret , normalizedURL )
950
957
if err != nil {
951
958
return nil , fmt .Errorf ("failed to create TLS client config for HelmRepository '%s': %w" , repo .Name , err )
952
959
}
953
- }
954
960
955
- chartRepo , err := repository .NewChartRepository (repo .Spec .URL , "" , r .Getters , tlsConfig , clientOpts )
956
- if err != nil {
957
- return nil , err
961
+ // Build registryClient options from secret
962
+ loginOpt , err := registry .LoginOptionFromSecret (normalizedURL , * secret )
963
+ if err != nil {
964
+ return nil , fmt .Errorf ("failed to create login options for HelmRepository '%s': %w" , repo .Name , err )
965
+ }
966
+
967
+ loginOpts = append ([]helmreg.LoginOption {}, loginOpt )
958
968
}
959
969
960
- // Ensure that the cache key is the same as the artifact path
961
- // otherwise don't enable caching. We don't want to cache indexes
962
- // for repositories that are not reconciled by the source controller.
963
- if repo .Status .Artifact != nil {
964
- chartRepo .CachePath = r .Storage .LocalPath (* repo .GetArtifact ())
965
- chartRepo .SetMemCache (r .Storage .LocalPath (* repo .GetArtifact ()), r .Cache , r .TTL , func (event string ) {
966
- r .IncCacheEvents (event , name , namespace )
967
- })
970
+ var chartRepo chart.CleanDownloader
971
+ if helmreg .IsOCI (normalizedURL ) {
972
+ registryClient , file , err := r .RegistryClientGenerator (loginOpts != nil )
973
+ if err != nil {
974
+ return nil , fmt .Errorf ("failed to create registry client for HelmRepository '%s': %w" , repo .Name , err )
975
+ }
976
+
977
+ var errs []error
978
+ // Tell the chart repository to use the OCI client with the configured getter
979
+ clientOpts = append (clientOpts , helmgetter .WithRegistryClient (registryClient ))
980
+ ociChartRepo , err := repository .NewOCIChartRepository (normalizedURL , repository .WithOCIGetter (r .Getters ),
981
+ repository .WithOCIGetterOptions (clientOpts ),
982
+ repository .WithOCIRegistryClient (registryClient ),
983
+ repository .WithCredentialFile (file ))
984
+ if err != nil {
985
+ // clean up the file
986
+ if err := os .Remove (file ); err != nil {
987
+ errs = append (errs , err )
988
+ }
989
+ errs = append (errs , fmt .Errorf ("failed to create OCI chart repository for HelmRepository '%s': %w" , repo .Name , err ))
990
+ return nil , kerrors .NewAggregate (errs )
991
+ }
992
+
993
+ // If login options are configured, use them to login to the registry
994
+ // The OCIGetter will later retrieve the stored credentials to pull the chart
995
+ if loginOpts != nil {
996
+ err = ociChartRepo .Login (loginOpts ... )
997
+ if err != nil {
998
+ return nil , fmt .Errorf ("failed to login to OCI chart repository for HelmRepository '%s': %w" , repo .Name , err )
999
+ }
1000
+ }
1001
+
1002
+ chartRepo = ociChartRepo
1003
+ } else {
1004
+ httpChartRepo , err := repository .NewChartRepository (normalizedURL , "" , r .Getters , tlsConfig , clientOpts )
1005
+ if err != nil {
1006
+ return nil , err
1007
+ }
1008
+
1009
+ // Ensure that the cache key is the same as the artifact path
1010
+ // otherwise don't enable caching. We don't want to cache indexes
1011
+ // for repositories that are not reconciled by the source controller.
1012
+ if repo .Status .Artifact != nil {
1013
+ httpChartRepo .CachePath = r .Storage .LocalPath (* repo .GetArtifact ())
1014
+ httpChartRepo .SetMemCache (r .Storage .LocalPath (* repo .GetArtifact ()), r .Cache , r .TTL , func (event string ) {
1015
+ r .IncCacheEvents (event , name , namespace )
1016
+ })
1017
+ }
1018
+
1019
+ chartRepo = httpChartRepo
968
1020
}
1021
+
969
1022
return chartRepo , nil
970
1023
}
971
1024
}
0 commit comments