Enable caching contextual Login Credentials
Provide an optional Cache to the Login Manager used to retrive contextual login credentials. Signed-off-by: Soule BA <bah.soule@gmail.com>
This commit is contained in:
parent
59ad5a72ee
commit
5ecc6b10a8
4
go.mod
4
go.mod
|
@ -22,13 +22,14 @@ require (
|
||||||
github.com/fluxcd/cli-utils v0.36.0-flux.7
|
github.com/fluxcd/cli-utils v0.36.0-flux.7
|
||||||
github.com/fluxcd/pkg/apis/event v0.9.0
|
github.com/fluxcd/pkg/apis/event v0.9.0
|
||||||
github.com/fluxcd/pkg/apis/meta v1.5.0
|
github.com/fluxcd/pkg/apis/meta v1.5.0
|
||||||
|
github.com/fluxcd/pkg/cache v0.0.1
|
||||||
github.com/fluxcd/pkg/git v0.19.0
|
github.com/fluxcd/pkg/git v0.19.0
|
||||||
github.com/fluxcd/pkg/git/gogit v0.19.0
|
github.com/fluxcd/pkg/git/gogit v0.19.0
|
||||||
github.com/fluxcd/pkg/gittestserver v0.12.0
|
github.com/fluxcd/pkg/gittestserver v0.12.0
|
||||||
github.com/fluxcd/pkg/helmtestserver v0.18.0
|
github.com/fluxcd/pkg/helmtestserver v0.18.0
|
||||||
github.com/fluxcd/pkg/lockedfile v0.3.0
|
github.com/fluxcd/pkg/lockedfile v0.3.0
|
||||||
github.com/fluxcd/pkg/masktoken v0.4.0
|
github.com/fluxcd/pkg/masktoken v0.4.0
|
||||||
github.com/fluxcd/pkg/oci v0.37.1
|
github.com/fluxcd/pkg/oci v0.38.0
|
||||||
github.com/fluxcd/pkg/runtime v0.47.1
|
github.com/fluxcd/pkg/runtime v0.47.1
|
||||||
github.com/fluxcd/pkg/sourceignore v0.7.0
|
github.com/fluxcd/pkg/sourceignore v0.7.0
|
||||||
github.com/fluxcd/pkg/ssh v0.13.0
|
github.com/fluxcd/pkg/ssh v0.13.0
|
||||||
|
@ -314,7 +315,6 @@ require (
|
||||||
github.com/spf13/cobra v1.8.0 // indirect
|
github.com/spf13/cobra v1.8.0 // indirect
|
||||||
github.com/spf13/viper v1.18.2 // indirect
|
github.com/spf13/viper v1.18.2 // indirect
|
||||||
github.com/spiffe/go-spiffe/v2 v2.2.0 // indirect
|
github.com/spiffe/go-spiffe/v2 v2.2.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.1 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||||
github.com/thales-e-security/pool v0.0.2 // indirect
|
github.com/thales-e-security/pool v0.0.2 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -342,6 +342,8 @@ github.com/fluxcd/pkg/apis/event v0.9.0 h1:iKxU+3v/3bAuC1C1iXg1mjbIiaEQet7WETh8l
|
||||||
github.com/fluxcd/pkg/apis/event v0.9.0/go.mod h1:5LjcTeppPMEyOgtTbIP7q2GbVwIRUfujIxynIjHBV/k=
|
github.com/fluxcd/pkg/apis/event v0.9.0/go.mod h1:5LjcTeppPMEyOgtTbIP7q2GbVwIRUfujIxynIjHBV/k=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.5.0 h1:/G82d2Az5D9op3F+wJUpD8jw/eTV0suM6P7+cSURoUM=
|
github.com/fluxcd/pkg/apis/meta v1.5.0 h1:/G82d2Az5D9op3F+wJUpD8jw/eTV0suM6P7+cSURoUM=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.5.0/go.mod h1:Y3u7JomuuKtr5fvP1Iji2/50FdRe5GcBug2jawNVkdM=
|
github.com/fluxcd/pkg/apis/meta v1.5.0/go.mod h1:Y3u7JomuuKtr5fvP1Iji2/50FdRe5GcBug2jawNVkdM=
|
||||||
|
github.com/fluxcd/pkg/cache v0.0.1 h1:aeDQm4D37btj6I01p6ZKW6JNOZm3CIYN5PaVzyhHr38=
|
||||||
|
github.com/fluxcd/pkg/cache v0.0.1/go.mod h1:R3TJIK9XaohHNc3BeqfZX/UivMrx8Xz6ihGoVAjh75k=
|
||||||
github.com/fluxcd/pkg/git v0.19.0 h1:zIv+GAT0ieIUpnGBVi3Bhax/qq4Rr28BW7Jv4DTt6zE=
|
github.com/fluxcd/pkg/git v0.19.0 h1:zIv+GAT0ieIUpnGBVi3Bhax/qq4Rr28BW7Jv4DTt6zE=
|
||||||
github.com/fluxcd/pkg/git v0.19.0/go.mod h1:wkqUOSrTjtsVVk/gC6/7RxVpi9GcqAA+7O5HVJF5S14=
|
github.com/fluxcd/pkg/git v0.19.0/go.mod h1:wkqUOSrTjtsVVk/gC6/7RxVpi9GcqAA+7O5HVJF5S14=
|
||||||
github.com/fluxcd/pkg/git/gogit v0.19.0 h1:SdoNAmC/HTPXniQjp609X59rCsBiA+Sdq1Hv8SnYC6I=
|
github.com/fluxcd/pkg/git/gogit v0.19.0 h1:SdoNAmC/HTPXniQjp609X59rCsBiA+Sdq1Hv8SnYC6I=
|
||||||
|
@ -354,8 +356,8 @@ github.com/fluxcd/pkg/lockedfile v0.3.0 h1:tZkBAffcxyt4zMigHIKc54cKgN5I/kFF005gy
|
||||||
github.com/fluxcd/pkg/lockedfile v0.3.0/go.mod h1:5iCYXAs953LlXZq7nTId9ZSGnHVvTfZ0mDmrDE49upk=
|
github.com/fluxcd/pkg/lockedfile v0.3.0/go.mod h1:5iCYXAs953LlXZq7nTId9ZSGnHVvTfZ0mDmrDE49upk=
|
||||||
github.com/fluxcd/pkg/masktoken v0.4.0 h1:pRItymXzW8dhT9Fd4XfnbrgKeySPeeLCrr6W1pgrUbM=
|
github.com/fluxcd/pkg/masktoken v0.4.0 h1:pRItymXzW8dhT9Fd4XfnbrgKeySPeeLCrr6W1pgrUbM=
|
||||||
github.com/fluxcd/pkg/masktoken v0.4.0/go.mod h1:MP1nCsr2tJbH8hnhZP4+7TfTR0ggrKOJgi9Bo7Mj/6M=
|
github.com/fluxcd/pkg/masktoken v0.4.0/go.mod h1:MP1nCsr2tJbH8hnhZP4+7TfTR0ggrKOJgi9Bo7Mj/6M=
|
||||||
github.com/fluxcd/pkg/oci v0.37.1 h1:p4rfCHZlBWL+Q5Xey51iiBRmoje0IevCBT0/r8iae3M=
|
github.com/fluxcd/pkg/oci v0.38.0 h1:a9pCdqiUPZ7YOnYDXVXCxELBU0r6xbDnGv4C6YUz7vU=
|
||||||
github.com/fluxcd/pkg/oci v0.37.1/go.mod h1:LrVuX6VACenJ5ycQJxec+I7YJegCsE4nzRUV+6RuxcY=
|
github.com/fluxcd/pkg/oci v0.38.0/go.mod h1:mYVSxnpVutRmWu6mpwxm7hXFn6qdhLEjspL04ej/WZU=
|
||||||
github.com/fluxcd/pkg/runtime v0.47.1 h1:Q1tAFsp92uurWyoEe52AmMC4k+6DYTPBrUQDs+nz/9c=
|
github.com/fluxcd/pkg/runtime v0.47.1 h1:Q1tAFsp92uurWyoEe52AmMC4k+6DYTPBrUQDs+nz/9c=
|
||||||
github.com/fluxcd/pkg/runtime v0.47.1/go.mod h1:97a+PqpWMgQsoqh91uH3EQz+/DC7Uxc8xcu/rDHFC5c=
|
github.com/fluxcd/pkg/runtime v0.47.1/go.mod h1:97a+PqpWMgQsoqh91uH3EQz+/DC7Uxc8xcu/rDHFC5c=
|
||||||
github.com/fluxcd/pkg/sourceignore v0.7.0 h1:qQrB2o543wA1o4vgR62ufwkAaDp8+f8Wdj1HKDlmDrU=
|
github.com/fluxcd/pkg/sourceignore v0.7.0 h1:qQrB2o543wA1o4vgR62ufwkAaDp8+f8Wdj1HKDlmDrU=
|
||||||
|
@ -911,8 +913,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
|
|
@ -47,7 +47,7 @@ func NewCacheRecorder() *CacheRecorder {
|
||||||
return &CacheRecorder{
|
return &CacheRecorder{
|
||||||
cacheEventsCounter: prometheus.NewCounterVec(
|
cacheEventsCounter: prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "gotk_cache_events_total",
|
Name: "gotk_sc_cache_events_total",
|
||||||
Help: "Total number of cache retrieval events for a Gitops Toolkit resource reconciliation.",
|
Help: "Total number of cache retrieval events for a Gitops Toolkit resource reconciliation.",
|
||||||
},
|
},
|
||||||
[]string{"event_type", "name", "namespace"},
|
[]string{"event_type", "name", "namespace"},
|
||||||
|
|
|
@ -138,6 +138,7 @@ type HelmChartReconciler struct {
|
||||||
Getters helmgetter.Providers
|
Getters helmgetter.Providers
|
||||||
ControllerName string
|
ControllerName string
|
||||||
|
|
||||||
|
OIDCAuthenticator *soci.OIDCAuthenticator
|
||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
*cache.CacheRecorder
|
*cache.CacheRecorder
|
||||||
|
@ -527,7 +528,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
return chartRepoConfigErrorReturn(err, obj)
|
return chartRepoConfigErrorReturn(err, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOpts, certsTmpDir, err := getter.GetClientOpts(ctxTimeout, r.Client, repo, normalizedURL)
|
clientOpts, certsTmpDir, err := getter.GetClientOptsWithOIDCAuth(ctxTimeout,
|
||||||
|
r.Client, r.OIDCAuthenticator, repo, normalizedURL)
|
||||||
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
|
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
|
||||||
e := serror.NewGeneric(
|
e := serror.NewGeneric(
|
||||||
err,
|
err,
|
||||||
|
@ -1014,6 +1016,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
URL: url,
|
URL: url,
|
||||||
Timeout: &metav1.Duration{Duration: 60 * time.Second},
|
Timeout: &metav1.Duration{Duration: 60 * time.Second},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1022,7 +1025,8 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
|
||||||
ctxTimeout, cancel := context.WithTimeout(ctx, obj.GetTimeout())
|
ctxTimeout, cancel := context.WithTimeout(ctx, obj.GetTimeout())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
clientOpts, certsTmpDir, err := getter.GetClientOpts(ctxTimeout, r.Client, obj, normalizedURL)
|
clientOpts, certsTmpDir, err := getter.GetClientOptsWithOIDCAuth(ctxTimeout, r.Client,
|
||||||
|
r.OIDCAuthenticator, obj, normalizedURL)
|
||||||
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
|
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1131,6 +1131,7 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
URL: server.URL(),
|
URL: server.URL(),
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
Status: sourcev1.HelmRepositoryStatus{
|
Status: sourcev1.HelmRepositoryStatus{
|
||||||
Artifact: &sourcev1.Artifact{
|
Artifact: &sourcev1.Artifact{
|
||||||
|
@ -2647,11 +2648,14 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authenticator, er := oci.NewOIDCAuthenticator(oci.WithCacheCapacity(1))
|
||||||
|
g.Expect(er).NotTo(HaveOccurred())
|
||||||
r := &HelmChartReconciler{
|
r := &HelmChartReconciler{
|
||||||
Client: clientBuilder.Build(),
|
Client: clientBuilder.Build(),
|
||||||
EventRecorder: record.NewFakeRecorder(32),
|
EventRecorder: record.NewFakeRecorder(32),
|
||||||
Getters: testGetters,
|
Getters: testGetters,
|
||||||
RegistryClientGenerator: registry.ClientGenerator,
|
RegistryClientGenerator: registry.ClientGenerator,
|
||||||
|
OIDCAuthenticator: authenticator,
|
||||||
patchOptions: getPatchOptions(helmChartReadyCondition.Owned, "sc"),
|
patchOptions: getPatchOptions(helmChartReadyCondition.Owned, "sc"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ import (
|
||||||
"github.com/fluxcd/pkg/runtime/patch"
|
"github.com/fluxcd/pkg/runtime/patch"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
"github.com/fluxcd/source-controller/internal/cache"
|
"github.com/fluxcd/source-controller/internal/cache"
|
||||||
intdigest "github.com/fluxcd/source-controller/internal/digest"
|
intdigest "github.com/fluxcd/source-controller/internal/digest"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/getter"
|
"github.com/fluxcd/source-controller/internal/helm/getter"
|
||||||
|
@ -819,6 +820,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ type OCIRepositoryReconciler struct {
|
||||||
Storage *Storage
|
Storage *Storage
|
||||||
ControllerName string
|
ControllerName string
|
||||||
requeueDependency time.Duration
|
requeueDependency time.Duration
|
||||||
|
OIDCAuthenticator *soci.OIDCAuthenticator
|
||||||
|
|
||||||
patchOptions []patch.Option
|
patchOptions []patch.Option
|
||||||
}
|
}
|
||||||
|
@ -355,7 +356,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
|
||||||
|
|
||||||
if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != ociv1.GenericOCIProvider && ok {
|
if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != ociv1.GenericOCIProvider && ok {
|
||||||
var authErr error
|
var authErr error
|
||||||
auth, authErr = soci.OIDCAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider)
|
auth, authErr = r.OIDCAuthenticator.Authorization(ctxTimeout, obj.Spec.URL, obj.Spec.Provider)
|
||||||
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
|
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
|
||||||
e := serror.NewGeneric(
|
e := serror.NewGeneric(
|
||||||
fmt.Errorf("failed to get credential from %s: %w", obj.Spec.Provider, authErr),
|
fmt.Errorf("failed to get credential from %s: %w", obj.Spec.Provider, authErr),
|
||||||
|
|
|
@ -69,8 +69,10 @@ import (
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
ociv1 "github.com/fluxcd/source-controller/api/v1beta2"
|
ociv1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
intdigest "github.com/fluxcd/source-controller/internal/digest"
|
intdigest "github.com/fluxcd/source-controller/internal/digest"
|
||||||
serror "github.com/fluxcd/source-controller/internal/error"
|
serror "github.com/fluxcd/source-controller/internal/error"
|
||||||
|
soci "github.com/fluxcd/source-controller/internal/oci"
|
||||||
snotation "github.com/fluxcd/source-controller/internal/oci/notation"
|
snotation "github.com/fluxcd/source-controller/internal/oci/notation"
|
||||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||||
)
|
)
|
||||||
|
@ -795,10 +797,14 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
||||||
obj.Spec.Insecure = true
|
obj.Spec.Insecure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authenticator, er := soci.NewOIDCAuthenticator(soci.WithCacheCapacity(1))
|
||||||
|
g.Expect(er).NotTo(HaveOccurred())
|
||||||
|
|
||||||
r := &OCIRepositoryReconciler{
|
r := &OCIRepositoryReconciler{
|
||||||
Client: clientBuilder.Build(),
|
Client: clientBuilder.Build(),
|
||||||
EventRecorder: record.NewFakeRecorder(32),
|
EventRecorder: record.NewFakeRecorder(32),
|
||||||
Storage: testStorage,
|
Storage: testStorage,
|
||||||
|
OIDCAuthenticator: authenticator,
|
||||||
patchOptions: getPatchOptions(ociRepositoryReadyCondition.Owned, "sc"),
|
patchOptions: getPatchOptions(ociRepositoryReadyCondition.Owned, "sc"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,6 +1153,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1398,6 +1405,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1718,6 +1726,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2042,6 +2051,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2265,6 +2275,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
Reference: tt.reference,
|
Reference: tt.reference,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2448,6 +2459,7 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) {
|
||||||
Reference: &ociv1.OCIRepositoryRef{Tag: "6.1.5"},
|
Reference: &ociv1.OCIRepositoryRef{Tag: "6.1.5"},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: interval},
|
||||||
Timeout: &metav1.Duration{Duration: timeout},
|
Timeout: &metav1.Duration{Duration: timeout},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,13 +64,17 @@ func (o ClientOpts) MustLoginToRegistry() bool {
|
||||||
return len(o.RegLoginOpts) > 0 && o.RegLoginOpts[0] != nil
|
return len(o.RegLoginOpts) > 0 && o.RegLoginOpts[0] != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientOpts uses the provided HelmRepository object and a normalized
|
// GetClientOptsWithOIDCAuth uses the provided HelmRepository object and a normalized
|
||||||
// URL to construct a HelmClientOpts object. If obj is an OCI HelmRepository,
|
// URL to construct a HelmClientOpts object. If obj is an OCI HelmRepository,
|
||||||
// then the returned options object will also contain the required registry
|
// then the returned options object will also contain the required registry
|
||||||
// auth mechanisms.
|
// auth mechanisms.
|
||||||
// A temporary directory is created to store the certs files if needed and its path is returned along with the options object. It is the
|
// A temporary directory is created to store the certs files if needed and its path is returned along with the options object. It is the
|
||||||
// caller's responsibility to clean up the directory.
|
// caller's responsibility to clean up the directory.
|
||||||
func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepository, url string) (*ClientOpts, string, error) {
|
func GetClientOptsWithOIDCAuth(ctx context.Context, c client.Client, oidcAuthenticator *soci.OIDCAuthenticator,
|
||||||
|
obj *sourcev1.HelmRepository, url string) (*ClientOpts, string, error) {
|
||||||
|
if obj.Spec.Provider != sourcev1beta2.GenericOCIProvider && oidcAuthenticator == nil {
|
||||||
|
return nil, "", fmt.Errorf("OIDC authenticator is not configured")
|
||||||
|
}
|
||||||
hrOpts := &ClientOpts{
|
hrOpts := &ClientOpts{
|
||||||
GetterOpts: []helmgetter.Option{
|
GetterOpts: []helmgetter.Option{
|
||||||
helmgetter.WithURL(url),
|
helmgetter.WithURL(url),
|
||||||
|
@ -137,7 +141,7 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if obj.Spec.Provider != sourcev1beta2.GenericOCIProvider && obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI && ociRepo {
|
} else if obj.Spec.Provider != sourcev1beta2.GenericOCIProvider && obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI && ociRepo {
|
||||||
authenticator, authErr := soci.OIDCAuth(ctx, obj.Spec.URL, obj.Spec.Provider)
|
authenticator, authErr := oidcAuthenticator.Authorization(ctx, obj.Spec.URL, obj.Spec.Provider)
|
||||||
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
|
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
|
||||||
return nil, "", fmt.Errorf("failed to get credential from '%s': %w", obj.Spec.Provider, authErr)
|
return nil, "", fmt.Errorf("failed to get credential from '%s': %w", obj.Spec.Provider, authErr)
|
||||||
}
|
}
|
||||||
|
@ -179,6 +183,16 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepos
|
||||||
return hrOpts, dir, err
|
return hrOpts, dir, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClientOpts uses the provided HelmRepository object and a normalized
|
||||||
|
// URL to construct a HelmClientOpts object. If obj is an OCI HelmRepository,
|
||||||
|
// then the returned options object will also contain the required registry
|
||||||
|
// auth mechanisms.
|
||||||
|
// A temporary directory is created to store the certs files if needed and its path is returned along with the options object. It is the
|
||||||
|
// caller's responsibility to clean up the directory.
|
||||||
|
func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepository, url string) (*ClientOpts, string, error) {
|
||||||
|
return GetClientOptsWithOIDCAuth(ctx, c, nil, obj, url)
|
||||||
|
}
|
||||||
|
|
||||||
func fetchSecret(ctx context.Context, c client.Client, name, namespace string) (*corev1.Secret, error) {
|
func fetchSecret(ctx context.Context, c client.Client, name, namespace string) (*corev1.Secret, error) {
|
||||||
key := types.NamespacedName{
|
key := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
helmv1 "github.com/fluxcd/source-controller/api/v1"
|
helmv1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetClientOpts(t *testing.T) {
|
func TestGetClientOpts(t *testing.T) {
|
||||||
|
@ -143,6 +144,7 @@ func TestGetClientOpts(t *testing.T) {
|
||||||
Duration: time.Second,
|
Duration: time.Second,
|
||||||
},
|
},
|
||||||
Insecure: tt.insecure,
|
Insecure: tt.insecure,
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if tt.oci {
|
if tt.oci {
|
||||||
|
@ -249,6 +251,7 @@ func TestGetClientOpts_registryTLSLoginOption(t *testing.T) {
|
||||||
Timeout: &metav1.Duration{
|
Timeout: &metav1.Duration{
|
||||||
Duration: time.Second,
|
Duration: time.Second,
|
||||||
},
|
},
|
||||||
|
Provider: sourcev1beta2.GenericOCIProvider,
|
||||||
Type: helmv1.HelmRepositoryTypeOCI,
|
Type: helmv1.HelmRepositoryTypeOCI,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,30 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/cache"
|
||||||
"github.com/fluxcd/pkg/oci/auth/login"
|
"github.com/fluxcd/pkg/oci/auth/login"
|
||||||
"github.com/google/go-containerregistry/pkg/authn"
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// We want to cache the authenticators for the 3rd party providers
|
||||||
|
// There are at least 3 providers (aws, azure, gcp), but there could be more
|
||||||
|
// e.g. alibaba, ibm, etc. But realistically, we can expect the number of
|
||||||
|
// providers to be less than 10.
|
||||||
|
DefaultAuthCacheCapacity = 10
|
||||||
|
// The cache cleanup interval, to remove expired entries
|
||||||
|
// 1 minute is a reasonable interval for authentication tokens.
|
||||||
|
// We don't want to be aggressive with the cleanup, as the tokens
|
||||||
|
// are valid for a longer period of time usually.
|
||||||
|
defaultAuthCacheInterval = time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
// Anonymous is an authn.AuthConfig that always returns an anonymous
|
// Anonymous is an authn.AuthConfig that always returns an anonymous
|
||||||
// authenticator. It is useful for registries that do not require authentication
|
// authenticator. It is useful for registries that do not require authentication
|
||||||
// or when the credentials are not known.
|
// or when the credentials are not known.
|
||||||
|
@ -39,15 +55,62 @@ func (a Anonymous) Resolve(_ authn.Resource) (authn.Authenticator, error) {
|
||||||
return authn.Anonymous, nil
|
return authn.Anonymous, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCAuth generates the OIDC credential authenticator based on the specified cloud provider.
|
// OIDCAuthenticatorOptionFunc is a functional option for the OIDCAuthenticator.
|
||||||
func OIDCAuth(ctx context.Context, url, provider string) (authn.Authenticator, error) {
|
type OIDCAuthenticatorOptionFunc func(opts *oidcAuthenticatorOptions)
|
||||||
|
|
||||||
|
type oidcAuthenticatorOptions struct {
|
||||||
|
capacity int
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCacheCapacity sets the capacity of the cache.
|
||||||
|
func WithCacheCapacity(capacity int) OIDCAuthenticatorOptionFunc {
|
||||||
|
return func(opts *oidcAuthenticatorOptions) {
|
||||||
|
opts.capacity = capacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCAuthenticator holds a manager for the OIDC authenticators.
|
||||||
|
// It caches the authenticators to avoid re-authenticating for the same URL.
|
||||||
|
type OIDCAuthenticator struct {
|
||||||
|
manager *login.Manager
|
||||||
|
cache cache.Expirable[cache.StoreObject[authn.Authenticator]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOIDCAuthenticator returns a new OIDCAuthenticator.
|
||||||
|
// The capacity is the number of authenticators to cache.
|
||||||
|
// If the capacity is less than or equal to 0, the cache is disabled.
|
||||||
|
func NewOIDCAuthenticator(opts ...OIDCAuthenticatorOptionFunc) (*OIDCAuthenticator, error) {
|
||||||
|
o := &oidcAuthenticatorOptions{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
c cache.Expirable[cache.StoreObject[authn.Authenticator]]
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if o.capacity > 0 {
|
||||||
|
c, err = cache.New(o.capacity, cache.StoreObjectKeyFunc,
|
||||||
|
cache.WithCleanupInterval[cache.StoreObject[authn.Authenticator]](defaultAuthCacheInterval),
|
||||||
|
cache.WithMetricsRegisterer[cache.StoreObject[authn.Authenticator]](metrics.Registry))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create cache: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager := login.NewManager()
|
||||||
|
return &OIDCAuthenticator{cache: c, manager: manager}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization returns an authenticator for the OIDC credentials.
|
||||||
|
func (o *OIDCAuthenticator) Authorization(ctx context.Context, url, provider string) (authn.Authenticator, error) {
|
||||||
u := strings.TrimPrefix(url, sourcev1.OCIRepositoryPrefix)
|
u := strings.TrimPrefix(url, sourcev1.OCIRepositoryPrefix)
|
||||||
ref, err := name.ParseReference(u)
|
ref, err := name.ParseReference(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse URL '%s': %w", u, err)
|
return nil, fmt.Errorf("failed to parse URL '%s': %w", u, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := login.ProviderOptions{}
|
opts := login.ProviderOptions{Cache: o.cache}
|
||||||
switch provider {
|
switch provider {
|
||||||
case sourcev1.AmazonOCIProvider:
|
case sourcev1.AmazonOCIProvider:
|
||||||
opts.AwsAutoLogin = true
|
opts.AwsAutoLogin = true
|
||||||
|
@ -57,5 +120,5 @@ func OIDCAuth(ctx context.Context, url, provider string) (authn.Authenticator, e
|
||||||
opts.GcpAutoLogin = true
|
opts.GcpAutoLogin = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return login.NewManager().Login(ctx, u, ref, opts)
|
return o.manager.Login(ctx, u, ref, opts)
|
||||||
}
|
}
|
||||||
|
|
21
main.go
21
main.go
|
@ -50,7 +50,7 @@ import (
|
||||||
"github.com/fluxcd/pkg/runtime/pprof"
|
"github.com/fluxcd/pkg/runtime/pprof"
|
||||||
"github.com/fluxcd/pkg/runtime/probes"
|
"github.com/fluxcd/pkg/runtime/probes"
|
||||||
|
|
||||||
"github.com/fluxcd/source-controller/api/v1"
|
v1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
"github.com/fluxcd/source-controller/api/v1beta2"
|
"github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
// +kubebuilder:scaffold:imports
|
// +kubebuilder:scaffold:imports
|
||||||
|
@ -61,6 +61,7 @@ import (
|
||||||
"github.com/fluxcd/source-controller/internal/features"
|
"github.com/fluxcd/source-controller/internal/features"
|
||||||
"github.com/fluxcd/source-controller/internal/helm"
|
"github.com/fluxcd/source-controller/internal/helm"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/registry"
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
|
"github.com/fluxcd/source-controller/internal/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
const controllerName = "source-controller"
|
const controllerName = "source-controller"
|
||||||
|
@ -187,6 +188,8 @@ func main() {
|
||||||
mustSetupHelmLimits(helmIndexLimit, helmChartLimit, helmChartFileLimit)
|
mustSetupHelmLimits(helmIndexLimit, helmChartLimit, helmChartFileLimit)
|
||||||
helmIndexCache, helmIndexCacheItemTTL := mustInitHelmCache(helmCacheMaxSize, helmCacheTTL, helmCachePurgeInterval)
|
helmIndexCache, helmIndexCacheItemTTL := mustInitHelmCache(helmCacheMaxSize, helmCacheTTL, helmCachePurgeInterval)
|
||||||
|
|
||||||
|
authenticator := mustInitOIDCAuthenticator()
|
||||||
|
|
||||||
ctx := ctrl.SetupSignalHandler()
|
ctx := ctrl.SetupSignalHandler()
|
||||||
|
|
||||||
if err := (&controller.GitRepositoryReconciler{
|
if err := (&controller.GitRepositoryReconciler{
|
||||||
|
@ -228,6 +231,7 @@ func main() {
|
||||||
EventRecorder: eventRecorder,
|
EventRecorder: eventRecorder,
|
||||||
Metrics: metrics,
|
Metrics: metrics,
|
||||||
ControllerName: controllerName,
|
ControllerName: controllerName,
|
||||||
|
OIDCAuthenticator: authenticator,
|
||||||
Cache: helmIndexCache,
|
Cache: helmIndexCache,
|
||||||
TTL: helmIndexCacheItemTTL,
|
TTL: helmIndexCacheItemTTL,
|
||||||
CacheRecorder: cacheRecorder,
|
CacheRecorder: cacheRecorder,
|
||||||
|
@ -257,6 +261,7 @@ func main() {
|
||||||
EventRecorder: eventRecorder,
|
EventRecorder: eventRecorder,
|
||||||
ControllerName: controllerName,
|
ControllerName: controllerName,
|
||||||
Metrics: metrics,
|
Metrics: metrics,
|
||||||
|
OIDCAuthenticator: authenticator,
|
||||||
}).SetupWithManagerAndOptions(mgr, controller.OCIRepositoryReconcilerOptions{
|
}).SetupWithManagerAndOptions(mgr, controller.OCIRepositoryReconcilerOptions{
|
||||||
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -406,6 +411,20 @@ func mustInitHelmCache(maxSize int, itemTTL, purgeInterval string) (*cache.Cache
|
||||||
return cache.New(maxSize, interval), ttl
|
return cache.New(maxSize, interval), ttl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustInitOIDCAuthenticator() *oci.OIDCAuthenticator {
|
||||||
|
capacity := oci.DefaultAuthCacheCapacity
|
||||||
|
disabled, found := os.LookupEnv("LOGIN_CACHE_DISABLED")
|
||||||
|
if found && disabled == "true" {
|
||||||
|
capacity = -1
|
||||||
|
}
|
||||||
|
authenticator, err := oci.NewOIDCAuthenticator(oci.WithCacheCapacity(capacity))
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "unable to initialise OIDC authenticator")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return authenticator
|
||||||
|
}
|
||||||
|
|
||||||
func mustInitStorage(path string, storageAdvAddr string, artifactRetentionTTL time.Duration, artifactRetentionRecords int, artifactDigestAlgo string) *controller.Storage {
|
func mustInitStorage(path string, storageAdvAddr string, artifactRetentionTTL time.Duration, artifactRetentionRecords int, artifactDigestAlgo string) *controller.Storage {
|
||||||
if storageAdvAddr == "" {
|
if storageAdvAddr == "" {
|
||||||
storageAdvAddr = determineAdvStorageAddr(storageAdvAddr)
|
storageAdvAddr = determineAdvStorageAddr(storageAdvAddr)
|
||||||
|
|
Loading…
Reference in New Issue