Skip to content

Commit 3649d27

Browse files
committed
controllers: RFC-0005 fmt for HelmRepository rev
This includes changes to the `ChartRepository`, to allow calculating the revision and digest and tidy things. In addition, the responsibility of caching the `IndexFile` has been moved to the reconcilers. As this allowed to remove a lot of complexities within the `ChartRepository`, and prevented passing on the cache in general. Change `HelmRepository`'s Revision to digest Signed-off-by: Hidde Beydals <hello@hidde.co>
1 parent 93ec32d commit 3649d27

7 files changed

+841
-670
lines changed

controllers/helmchart_controller.go

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,20 @@ import (
2121
"crypto/tls"
2222
"errors"
2323
"fmt"
24-
"github.com/fluxcd/pkg/git"
25-
"github.com/opencontainers/go-digest"
24+
2625
"net/url"
2726
"os"
2827
"path/filepath"
2928
"strconv"
3029
"strings"
3130
"time"
3231

33-
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
34-
soci "github.com/fluxcd/source-controller/internal/oci"
3532
"github.com/google/go-containerregistry/pkg/authn"
3633
"github.com/google/go-containerregistry/pkg/v1/remote"
34+
"github.com/opencontainers/go-digest"
3735
helmgetter "helm.sh/helm/v3/pkg/getter"
3836
helmreg "helm.sh/helm/v3/pkg/registry"
37+
helmrepo "helm.sh/helm/v3/pkg/repo"
3938
corev1 "k8s.io/api/core/v1"
4039
apierrs "k8s.io/apimachinery/pkg/api/errors"
4140
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -54,7 +53,9 @@ import (
5453
"sigs.k8s.io/controller-runtime/pkg/reconcile"
5554
"sigs.k8s.io/controller-runtime/pkg/source"
5655

56+
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
5757
"github.com/fluxcd/pkg/apis/meta"
58+
"github.com/fluxcd/pkg/git"
5859
"github.com/fluxcd/pkg/oci"
5960
"github.com/fluxcd/pkg/runtime/conditions"
6061
helper "github.com/fluxcd/pkg/runtime/controller"
@@ -70,6 +71,7 @@ import (
7071
"github.com/fluxcd/source-controller/internal/helm/getter"
7172
"github.com/fluxcd/source-controller/internal/helm/registry"
7273
"github.com/fluxcd/source-controller/internal/helm/repository"
74+
soci "github.com/fluxcd/source-controller/internal/oci"
7375
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
7476
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
7577
"github.com/fluxcd/source-controller/internal/util"
@@ -527,7 +529,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
527529
}
528530

529531
// Build client options from secret
530-
opts, tls, err := r.clientOptionsFromSecret(secret, normalizedURL)
532+
opts, tlsCfg, err := r.clientOptionsFromSecret(secret, normalizedURL)
531533
if err != nil {
532534
e := &serror.Event{
533535
Err: err,
@@ -538,7 +540,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
538540
return sreconcile.ResultEmpty, e
539541
}
540542
clientOpts = append(clientOpts, opts...)
541-
tlsConfig = tls
543+
tlsConfig = tlsCfg
542544

543545
// Build registryClient options from secret
544546
keychain, err = registry.LoginOptionFromSecret(normalizedURL, *secret)
@@ -651,34 +653,26 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
651653
}
652654
}
653655
default:
654-
httpChartRepo, err := repository.NewChartRepository(normalizedURL, r.Storage.LocalPath(*repo.GetArtifact()), r.Getters, tlsConfig, clientOpts,
655-
repository.WithMemoryCache(r.Storage.LocalPath(*repo.GetArtifact()), r.Cache, r.TTL, func(event string) {
656-
r.IncCacheEvents(event, obj.Name, obj.Namespace)
657-
}))
656+
httpChartRepo, err := repository.NewChartRepository(normalizedURL, r.Storage.LocalPath(*repo.GetArtifact()), r.Getters, tlsConfig, clientOpts...)
658657
if err != nil {
659658
return chartRepoConfigErrorReturn(err, obj)
660659
}
661660
chartRepo = httpChartRepo
662661
defer func() {
663-
if httpChartRepo == nil {
664-
return
665-
}
666662
// Cache the index if it was successfully retrieved
667-
// and the chart was successfully built
663+
// and the chart was successfully built.
668664
if r.Cache != nil && httpChartRepo.Index != nil {
669-
// The cache key have to be safe in multi-tenancy environments,
670-
// as otherwise it could be used as a vector to bypass the helm repository's authentication.
671-
// Using r.Storage.LocalPath(*repo.GetArtifact() is safe as the path is in the format /<helm-repository-name>/<chart-name>/<filename>.
672-
err := httpChartRepo.CacheIndexInMemory()
673-
if err != nil {
665+
// The cache key has to be safe in multi-tenancy environments,
666+
// as otherwise it could be used as a vector to bypass
667+
// authentication to the repository.
668+
// Using r.Storage.LocalPath(*repo.GetArtifact()) is safe as the
669+
// path is in the format of: /<repository-name>/<chart-name>/<filename>.
670+
if err = r.Cache.Set(r.Storage.LocalPath(*repo.GetArtifact()), httpChartRepo.Index, r.TTL); err != nil {
674671
r.eventLogf(ctx, obj, eventv1.EventTypeTrace, sourcev1.CacheOperationFailedReason, "failed to cache index: %s", err)
675672
}
676673
}
677-
678674
// Delete the index reference
679-
if httpChartRepo.Index != nil {
680-
httpChartRepo.Unload()
681-
}
675+
httpChartRepo.Clear()
682676
}()
683677
}
684678

@@ -845,7 +839,7 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
845839
// early.
846840
// On a successful archive, the Artifact in the Status of the object is set,
847841
// and the symlink in the Storage is updated to its path.
848-
func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmChart, b *chart.Build) (sreconcile.Result, error) {
842+
func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, _ *patch.SerialPatcher, obj *sourcev1.HelmChart, b *chart.Build) (sreconcile.Result, error) {
849843
// Without a complete chart build, there is little to reconcile
850844
if !b.Complete() {
851845
return sreconcile.ResultRequeue, nil
@@ -1016,14 +1010,15 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10161010
authenticator authn.Authenticator
10171011
keychain authn.Keychain
10181012
)
1013+
10191014
normalizedURL := repository.NormalizeURL(url)
1020-
repo, err := r.resolveDependencyRepository(ctx, url, namespace)
1015+
obj, err := r.resolveDependencyRepository(ctx, url, namespace)
10211016
if err != nil {
10221017
// Return Kubernetes client errors, but ignore others
10231018
if apierrs.ReasonForError(err) != metav1.StatusReasonUnknown {
10241019
return nil, err
10251020
}
1026-
repo = &sourcev1.HelmRepository{
1021+
obj = &sourcev1.HelmRepository{
10271022
Spec: sourcev1.HelmRepositorySpec{
10281023
URL: url,
10291024
Timeout: &metav1.Duration{Duration: 60 * time.Second},
@@ -1032,37 +1027,37 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10321027
}
10331028

10341029
// Used to login with the repository declared provider
1035-
ctxTimeout, cancel := context.WithTimeout(ctx, repo.Spec.Timeout.Duration)
1030+
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
10361031
defer cancel()
10371032

10381033
clientOpts := []helmgetter.Option{
10391034
helmgetter.WithURL(normalizedURL),
1040-
helmgetter.WithTimeout(repo.Spec.Timeout.Duration),
1041-
helmgetter.WithPassCredentialsAll(repo.Spec.PassCredentials),
1035+
helmgetter.WithTimeout(obj.Spec.Timeout.Duration),
1036+
helmgetter.WithPassCredentialsAll(obj.Spec.PassCredentials),
10421037
}
1043-
if secret, err := r.getHelmRepositorySecret(ctx, repo); secret != nil || err != nil {
1038+
if secret, err := r.getHelmRepositorySecret(ctx, obj); secret != nil || err != nil {
10441039
if err != nil {
10451040
return nil, err
10461041
}
10471042

10481043
// Build client options from secret
1049-
opts, tls, err := r.clientOptionsFromSecret(secret, normalizedURL)
1044+
opts, tlsCfg, err := r.clientOptionsFromSecret(secret, normalizedURL)
10501045
if err != nil {
10511046
return nil, err
10521047
}
10531048
clientOpts = append(clientOpts, opts...)
1054-
tlsConfig = tls
1049+
tlsConfig = tlsCfg
10551050

10561051
// Build registryClient options from secret
10571052
keychain, err = registry.LoginOptionFromSecret(normalizedURL, *secret)
10581053
if err != nil {
1059-
return nil, fmt.Errorf("failed to create login options for HelmRepository '%s': %w", repo.Name, err)
1054+
return nil, fmt.Errorf("failed to create login options for HelmRepository '%s': %w", obj.Name, err)
10601055
}
10611056

1062-
} else if repo.Spec.Provider != sourcev1.GenericOCIProvider && repo.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
1063-
auth, authErr := oidcAuth(ctxTimeout, repo.Spec.URL, repo.Spec.Provider)
1057+
} else if obj.Spec.Provider != sourcev1.GenericOCIProvider && obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
1058+
auth, authErr := oidcAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider)
10641059
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
1065-
return nil, fmt.Errorf("failed to get credential from %s: %w", repo.Spec.Provider, authErr)
1060+
return nil, fmt.Errorf("failed to get credential from %s: %w", obj.Spec.Provider, authErr)
10661061
}
10671062
if auth != nil {
10681063
authenticator = auth
@@ -1078,7 +1073,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10781073
if helmreg.IsOCI(normalizedURL) {
10791074
registryClient, credentialsFile, err := r.RegistryClientGenerator(loginOpt != nil)
10801075
if err != nil {
1081-
return nil, fmt.Errorf("failed to create registry client for HelmRepository '%s': %w", repo.Name, err)
1076+
return nil, fmt.Errorf("failed to create registry client for HelmRepository '%s': %w", obj.Name, err)
10821077
}
10831078

10841079
var errs []error
@@ -1089,7 +1084,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10891084
repository.WithOCIRegistryClient(registryClient),
10901085
repository.WithCredentialsFile(credentialsFile))
10911086
if err != nil {
1092-
errs = append(errs, fmt.Errorf("failed to create OCI chart repository for HelmRepository '%s': %w", repo.Name, err))
1087+
errs = append(errs, fmt.Errorf("failed to create OCI chart repository for HelmRepository '%s': %w", obj.Name, err))
10931088
// clean up the credentialsFile
10941089
if credentialsFile != "" {
10951090
if err := os.Remove(credentialsFile); err != nil {
@@ -1104,7 +1099,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
11041099
if loginOpt != nil {
11051100
err = ociChartRepo.Login(loginOpt)
11061101
if err != nil {
1107-
errs = append(errs, fmt.Errorf("failed to login to OCI chart repository for HelmRepository '%s': %w", repo.Name, err))
1102+
errs = append(errs, fmt.Errorf("failed to login to OCI chart repository for HelmRepository '%s': %w", obj.Name, err))
11081103
// clean up the credentialsFile
11091104
errs = append(errs, ociChartRepo.Clear())
11101105
return nil, kerrors.NewAggregate(errs)
@@ -1113,19 +1108,28 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
11131108

11141109
chartRepo = ociChartRepo
11151110
} else {
1116-
httpChartRepo, err := repository.NewChartRepository(normalizedURL, "", r.Getters, tlsConfig, clientOpts)
1111+
httpChartRepo, err := repository.NewChartRepository(normalizedURL, "", r.Getters, tlsConfig, clientOpts...)
11171112
if err != nil {
11181113
return nil, err
11191114
}
11201115

1121-
// Ensure that the cache key is the same as the artifact path
1122-
// otherwise don't enable caching. We don't want to cache indexes
1123-
// for repositories that are not reconciled by the source controller.
1124-
if repo.Status.Artifact != nil {
1125-
httpChartRepo.CachePath = r.Storage.LocalPath(*repo.GetArtifact())
1126-
httpChartRepo.SetMemCache(r.Storage.LocalPath(*repo.GetArtifact()), r.Cache, r.TTL, func(event string) {
1127-
r.IncCacheEvents(event, name, namespace)
1128-
})
1116+
if obj.Status.Artifact != nil {
1117+
// Attempt to load the index from the cache.
1118+
httpChartRepo.Path = r.Storage.LocalPath(*obj.GetArtifact())
1119+
if r.Cache != nil {
1120+
if index, ok := r.Cache.Get(httpChartRepo.Path); ok {
1121+
r.IncCacheEvents(cache.CacheEventTypeHit, name, namespace)
1122+
r.Cache.SetExpiration(httpChartRepo.Path, r.TTL)
1123+
1124+
httpChartRepo.Index = index.(*helmrepo.IndexFile)
1125+
} else {
1126+
r.IncCacheEvents(cache.CacheEventTypeMiss, name, namespace)
1127+
if err := httpChartRepo.LoadFromPath(); err != nil {
1128+
return nil, err
1129+
}
1130+
r.Cache.Set(httpChartRepo.Path, httpChartRepo.Index, r.TTL)
1131+
}
1132+
}
11291133
}
11301134

11311135
chartRepo = httpChartRepo

0 commit comments

Comments
 (0)