Merge pull request #725 from fluxcd/oci-dockerconfig
Support dockerconfigjson with OCI HelmRepositories
This commit is contained in:
commit
351b21be84
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
helmgetter "helm.sh/helm/v3/pkg/getter"
|
helmgetter "helm.sh/helm/v3/pkg/getter"
|
||||||
"helm.sh/helm/v3/pkg/registry"
|
helmreg "helm.sh/helm/v3/pkg/registry"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
@ -60,6 +60,7 @@ import (
|
||||||
serror "github.com/fluxcd/source-controller/internal/error"
|
serror "github.com/fluxcd/source-controller/internal/error"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/chart"
|
"github.com/fluxcd/source-controller/internal/helm/chart"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/getter"
|
"github.com/fluxcd/source-controller/internal/helm/getter"
|
||||||
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/repository"
|
"github.com/fluxcd/source-controller/internal/helm/repository"
|
||||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||||
|
|
@ -380,7 +381,7 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, obj *sourcev1
|
||||||
|
|
||||||
// Assert source has an artifact
|
// Assert source has an artifact
|
||||||
if s.GetArtifact() == nil || !r.Storage.ArtifactExist(*s.GetArtifact()) {
|
if s.GetArtifact() == nil || !r.Storage.ArtifactExist(*s.GetArtifact()) {
|
||||||
if helmRepo, ok := s.(*sourcev1.HelmRepository); !ok || !registry.IsOCI(helmRepo.Spec.URL) {
|
if helmRepo, ok := s.(*sourcev1.HelmRepository); !ok || !helmreg.IsOCI(helmRepo.Spec.URL) {
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, "NoSourceArtifact",
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, "NoSourceArtifact",
|
||||||
"no artifact available for %s source '%s'", obj.Spec.SourceRef.Kind, obj.Spec.SourceRef.Name)
|
"no artifact available for %s source '%s'", obj.Spec.SourceRef.Kind, obj.Spec.SourceRef.Name)
|
||||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "NoSourceArtifact",
|
r.eventLogf(ctx, obj, events.EventTypeTrace, "NoSourceArtifact",
|
||||||
|
|
@ -447,7 +448,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
repo *sourcev1.HelmRepository, b *chart.Build) (sreconcile.Result, error) {
|
repo *sourcev1.HelmRepository, b *chart.Build) (sreconcile.Result, error) {
|
||||||
var (
|
var (
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
logOpts []registry.LoginOption
|
loginOpts []helmreg.LoginOption
|
||||||
)
|
)
|
||||||
|
|
||||||
// Construct the Getter options from the HelmRepository data
|
// Construct the Getter options from the HelmRepository data
|
||||||
|
|
@ -492,7 +493,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build registryClient options from secret
|
// Build registryClient options from secret
|
||||||
logOpt, err := loginOptionFromSecret(*secret)
|
loginOpt, err := registry.LoginOptionFromSecret(repo.Spec.URL, *secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := &serror.Event{
|
e := &serror.Event{
|
||||||
Err: fmt.Errorf("failed to configure Helm client with secret data: %w", err),
|
Err: fmt.Errorf("failed to configure Helm client with secret data: %w", err),
|
||||||
|
|
@ -503,14 +504,14 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
}
|
}
|
||||||
|
|
||||||
logOpts = append([]registry.LoginOption{}, logOpt)
|
loginOpts = append([]helmreg.LoginOption{}, loginOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the chart repository
|
// Initialize the chart repository
|
||||||
var chartRepo chart.Remote
|
var chartRepo chart.Remote
|
||||||
switch repo.Spec.Type {
|
switch repo.Spec.Type {
|
||||||
case sourcev1.HelmRepositoryTypeOCI:
|
case sourcev1.HelmRepositoryTypeOCI:
|
||||||
if !registry.IsOCI(repo.Spec.URL) {
|
if !helmreg.IsOCI(repo.Spec.URL) {
|
||||||
err := fmt.Errorf("invalid OCI registry URL: %s", repo.Spec.URL)
|
err := fmt.Errorf("invalid OCI registry URL: %s", repo.Spec.URL)
|
||||||
return chartRepoErrorReturn(err, obj)
|
return chartRepoErrorReturn(err, obj)
|
||||||
}
|
}
|
||||||
|
|
@ -519,7 +520,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
// this is needed because otherwise the credentials are stored in ~/.docker/config.json.
|
// this is needed because otherwise the credentials are stored in ~/.docker/config.json.
|
||||||
// TODO@souleb: remove this once the registry move to Oras v2
|
// TODO@souleb: remove this once the registry move to Oras v2
|
||||||
// or rework to enable reusing credentials to avoid the unneccessary handshake operations
|
// or rework to enable reusing credentials to avoid the unneccessary handshake operations
|
||||||
registryClient, file, err := r.RegistryClientGenerator(logOpts != nil)
|
registryClient, file, err := r.RegistryClientGenerator(loginOpts != nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return chartRepoErrorReturn(err, obj)
|
return chartRepoErrorReturn(err, obj)
|
||||||
}
|
}
|
||||||
|
|
@ -540,14 +541,13 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
|
|
||||||
// If login options are configured, use them to login to the registry
|
// If login options are configured, use them to login to the registry
|
||||||
// The OCIGetter will later retrieve the stored credentials to pull the chart
|
// The OCIGetter will later retrieve the stored credentials to pull the chart
|
||||||
if logOpts != nil {
|
if loginOpts != nil {
|
||||||
err = ociChartRepo.Login(logOpts...)
|
err = ociChartRepo.Login(loginOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return chartRepoErrorReturn(err, obj)
|
return chartRepoErrorReturn(err, obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
var httpChartRepo *repository.ChartRepository
|
|
||||||
httpChartRepo, err := repository.NewChartRepository(repo.Spec.URL, r.Storage.LocalPath(*repo.GetArtifact()), r.Getters, tlsConfig, clientOpts,
|
httpChartRepo, err := repository.NewChartRepository(repo.Spec.URL, r.Storage.LocalPath(*repo.GetArtifact()), r.Getters, tlsConfig, clientOpts,
|
||||||
repository.WithMemoryCache(r.Storage.LocalPath(*repo.GetArtifact()), r.Cache, r.TTL, func(event string) {
|
repository.WithMemoryCache(r.Storage.LocalPath(*repo.GetArtifact()), r.Cache, r.TTL, func(event string) {
|
||||||
r.IncCacheEvents(event, obj.Name, obj.Namespace)
|
r.IncCacheEvents(event, obj.Name, obj.Namespace)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -35,7 +36,7 @@ import (
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
hchart "helm.sh/helm/v3/pkg/chart"
|
hchart "helm.sh/helm/v3/pkg/chart"
|
||||||
"helm.sh/helm/v3/pkg/chart/loader"
|
"helm.sh/helm/v3/pkg/chart/loader"
|
||||||
"helm.sh/helm/v3/pkg/registry"
|
helmreg "helm.sh/helm/v3/pkg/registry"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
@ -53,7 +54,7 @@ import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
serror "github.com/fluxcd/source-controller/internal/error"
|
serror "github.com/fluxcd/source-controller/internal/error"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/chart"
|
"github.com/fluxcd/source-controller/internal/helm/chart"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/util"
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||||
)
|
)
|
||||||
|
|
@ -792,8 +793,8 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) {
|
||||||
|
|
||||||
// Login to the registry
|
// Login to the registry
|
||||||
err := testRegistryserver.RegistryClient.Login(testRegistryserver.DockerRegistryHost,
|
err := testRegistryserver.RegistryClient.Login(testRegistryserver.DockerRegistryHost,
|
||||||
registry.LoginOptBasicAuth(testUsername, testPassword),
|
helmreg.LoginOptBasicAuth(testUsername, testPassword),
|
||||||
registry.LoginOptInsecure(true))
|
helmreg.LoginOptInsecure(true))
|
||||||
g.Expect(err).NotTo(HaveOccurred())
|
g.Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Load a test chart
|
// Load a test chart
|
||||||
|
|
@ -825,6 +826,35 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) {
|
||||||
assertFunc func(g *WithT, obj *sourcev1.HelmChart, build chart.Build)
|
assertFunc func(g *WithT, obj *sourcev1.HelmChart, build chart.Build)
|
||||||
cleanFunc func(g *WithT, build *chart.Build)
|
cleanFunc func(g *WithT, build *chart.Build)
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "Reconciles chart build with docker repository credentials",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "auth",
|
||||||
|
},
|
||||||
|
Type: corev1.SecretTypeDockerConfigJson,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
".dockerconfigjson": []byte(`{"auths":{"` +
|
||||||
|
testRegistryserver.DockerRegistryHost + `":{"` +
|
||||||
|
`auth":"` + base64.StdEncoding.EncodeToString([]byte(testUsername+":"+testPassword)) + `"}}}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeFunc: func(obj *sourcev1.HelmChart, repository *sourcev1.HelmRepository) {
|
||||||
|
obj.Spec.Chart = metadata.Name
|
||||||
|
obj.Spec.Version = metadata.Version
|
||||||
|
repository.Spec.SecretRef = &meta.LocalObjectReference{Name: "auth"}
|
||||||
|
},
|
||||||
|
want: sreconcile.ResultSuccess,
|
||||||
|
assertFunc: func(g *WithT, _ *sourcev1.HelmChart, build chart.Build) {
|
||||||
|
g.Expect(build.Name).To(Equal(metadata.Name))
|
||||||
|
g.Expect(build.Version).To(Equal(metadata.Version))
|
||||||
|
g.Expect(build.Path).ToNot(BeEmpty())
|
||||||
|
g.Expect(build.Path).To(BeARegularFile())
|
||||||
|
},
|
||||||
|
cleanFunc: func(g *WithT, build *chart.Build) {
|
||||||
|
g.Expect(os.Remove(build.Path)).To(Succeed())
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Reconciles chart build with repository credentials",
|
name: "Reconciles chart build with repository credentials",
|
||||||
secret: &corev1.Secret{
|
secret: &corev1.Secret{
|
||||||
|
|
@ -945,7 +975,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) {
|
||||||
EventRecorder: record.NewFakeRecorder(32),
|
EventRecorder: record.NewFakeRecorder(32),
|
||||||
Getters: testGetters,
|
Getters: testGetters,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
RegistryClientGenerator: util.RegistryClientGenerator,
|
RegistryClientGenerator: registry.ClientGenerator,
|
||||||
}
|
}
|
||||||
|
|
||||||
repository := &sourcev1.HelmRepository{
|
repository := &sourcev1.HelmRepository{
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@ import (
|
||||||
"github.com/fluxcd/pkg/runtime/predicates"
|
"github.com/fluxcd/pkg/runtime/predicates"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
serror "github.com/fluxcd/source-controller/internal/error"
|
serror "github.com/fluxcd/source-controller/internal/error"
|
||||||
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/repository"
|
"github.com/fluxcd/source-controller/internal/helm/repository"
|
||||||
intpredicates "github.com/fluxcd/source-controller/internal/predicates"
|
intpredicates "github.com/fluxcd/source-controller/internal/predicates"
|
||||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||||
helmgetter "helm.sh/helm/v3/pkg/getter"
|
helmgetter "helm.sh/helm/v3/pkg/getter"
|
||||||
"helm.sh/helm/v3/pkg/registry"
|
helmreg "helm.sh/helm/v3/pkg/registry"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
kuberecorder "k8s.io/client-go/tools/record"
|
kuberecorder "k8s.io/client-go/tools/record"
|
||||||
|
|
@ -91,7 +92,7 @@ type HelmRepositoryOCIReconciler struct {
|
||||||
// and an optional file name.
|
// and an optional file name.
|
||||||
// The file is used to store the registry client credentials.
|
// The file is used to store the registry client credentials.
|
||||||
// The caller is responsible for deleting the file.
|
// The caller is responsible for deleting the file.
|
||||||
type RegistryClientGeneratorFunc func(isLogin bool) (*registry.Client, string, error)
|
type RegistryClientGeneratorFunc func(isLogin bool) (*helmreg.Client, string, error)
|
||||||
|
|
||||||
// helmRepositoryOCIReconcileFunc is the function type for all the
|
// helmRepositoryOCIReconcileFunc is the function type for all the
|
||||||
// v1beta2.HelmRepository (sub)reconcile functions for OCI type. The type implementations
|
// v1beta2.HelmRepository (sub)reconcile functions for OCI type. The type implementations
|
||||||
|
|
@ -254,7 +255,7 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, obj *source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HelmRepositoryOCIReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
|
func (r *HelmRepositoryOCIReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
|
||||||
var logOpts []registry.LoginOption
|
var loginOpts []helmreg.LoginOption
|
||||||
// Configure any authentication related options
|
// Configure any authentication related options
|
||||||
if obj.Spec.SecretRef != nil {
|
if obj.Spec.SecretRef != nil {
|
||||||
// Attempt to retrieve secret
|
// Attempt to retrieve secret
|
||||||
|
|
@ -273,7 +274,7 @@ func (r *HelmRepositoryOCIReconciler) reconcileSource(ctx context.Context, obj *
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct actual options
|
// Construct actual options
|
||||||
logOpt, err := loginOptionFromSecret(secret)
|
loginOpt, err := registry.LoginOptionFromSecret(obj.Spec.URL, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := &serror.Event{
|
e := &serror.Event{
|
||||||
Err: fmt.Errorf("failed to configure Helm client with secret data: %w", err),
|
Err: fmt.Errorf("failed to configure Helm client with secret data: %w", err),
|
||||||
|
|
@ -284,10 +285,12 @@ func (r *HelmRepositoryOCIReconciler) reconcileSource(ctx context.Context, obj *
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
}
|
}
|
||||||
|
|
||||||
logOpts = append(logOpts, logOpt)
|
if loginOpt != nil {
|
||||||
|
loginOpts = append(loginOpts, loginOpt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result, err := r.validateSource(ctx, obj, logOpts...); err != nil || result == sreconcile.ResultEmpty {
|
if result, err := r.validateSource(ctx, obj, loginOpts...); err != nil || result == sreconcile.ResultEmpty {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,7 +299,7 @@ func (r *HelmRepositoryOCIReconciler) reconcileSource(ctx context.Context, obj *
|
||||||
|
|
||||||
// validateSource the HelmRepository object by checking the url and connecting to the underlying registry
|
// validateSource the HelmRepository object by checking the url and connecting to the underlying registry
|
||||||
// with he provided credentials.
|
// with he provided credentials.
|
||||||
func (r *HelmRepositoryOCIReconciler) validateSource(ctx context.Context, obj *sourcev1.HelmRepository, logOpts ...registry.LoginOption) (sreconcile.Result, error) {
|
func (r *HelmRepositoryOCIReconciler) validateSource(ctx context.Context, obj *sourcev1.HelmRepository, logOpts ...helmreg.LoginOption) (sreconcile.Result, error) {
|
||||||
registryClient, file, err := r.RegistryClientGenerator(logOpts != nil)
|
registryClient, file, err := r.RegistryClientGenerator(logOpts != nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := &serror.Stalling{
|
e := &serror.Stalling{
|
||||||
|
|
@ -349,14 +352,3 @@ func (r *HelmRepositoryOCIReconciler) validateSource(ctx context.Context, obj *s
|
||||||
|
|
||||||
return sreconcile.ResultSuccess, nil
|
return sreconcile.ResultSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginOptionFromSecret(secret corev1.Secret) (registry.LoginOption, error) {
|
|
||||||
username, password := string(secret.Data["username"]), string(secret.Data["password"])
|
|
||||||
switch {
|
|
||||||
case username == "" && password == "":
|
|
||||||
return nil, nil
|
|
||||||
case username == "" || password == "":
|
|
||||||
return nil, fmt.Errorf("invalid '%s' secret data: required fields 'username' and 'password'", secret.Name)
|
|
||||||
}
|
|
||||||
return registry.LoginOptBasicAuth(username, password), nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -34,99 +35,129 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHelmRepositoryOCIReconciler_Reconcile(t *testing.T) {
|
func TestHelmRepositoryOCIReconciler_Reconcile(t *testing.T) {
|
||||||
g := NewWithT(t)
|
tests := []struct {
|
||||||
|
name string
|
||||||
ns, err := testEnv.CreateNamespace(ctx, "helmrepository-oci-reconcile-test")
|
secretType corev1.SecretType
|
||||||
g.Expect(err).ToNot(HaveOccurred())
|
secretData map[string][]byte
|
||||||
defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }()
|
}{
|
||||||
|
{
|
||||||
secret := &corev1.Secret{
|
name: "valid auth data",
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
secretData: map[string][]byte{
|
||||||
GenerateName: "helmrepository-",
|
"username": []byte(testUsername),
|
||||||
Namespace: ns.Name,
|
"password": []byte(testPassword),
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
},
|
||||||
"username": []byte(testUsername),
|
{
|
||||||
"password": []byte(testPassword),
|
name: "no auth data",
|
||||||
},
|
secretData: nil,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
g.Expect(testEnv.CreateAndWait(ctx, secret)).To(Succeed())
|
name: "dockerconfigjson Secret",
|
||||||
|
secretType: corev1.SecretTypeDockerConfigJson,
|
||||||
obj := &sourcev1.HelmRepository{
|
secretData: map[string][]byte{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
".dockerconfigjson": []byte(`{"auths":{"` +
|
||||||
GenerateName: "helmrepository-oci-reconcile-",
|
testRegistryserver.DockerRegistryHost + `":{"` +
|
||||||
Namespace: ns.Name,
|
`auth":"` + base64.StdEncoding.EncodeToString([]byte(testUsername+":"+testPassword)) + `"}}}`),
|
||||||
},
|
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
|
||||||
Interval: metav1.Duration{Duration: interval},
|
|
||||||
URL: fmt.Sprintf("oci://%s", testRegistryserver.DockerRegistryHost),
|
|
||||||
SecretRef: &meta.LocalObjectReference{
|
|
||||||
Name: secret.Name,
|
|
||||||
},
|
},
|
||||||
Type: sourcev1.HelmRepositoryTypeOCI,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.Expect(testEnv.Create(ctx, obj)).To(Succeed())
|
|
||||||
|
|
||||||
key := client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
|
||||||
// Wait for finalizer to be set
|
ns, err := testEnv.CreateNamespace(ctx, "helmrepository-oci-reconcile-test")
|
||||||
g.Eventually(func() bool {
|
g.Expect(err).ToNot(HaveOccurred())
|
||||||
if err := testEnv.Get(ctx, key, obj); err != nil {
|
defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }()
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(obj.Finalizers) > 0
|
|
||||||
}, timeout).Should(BeTrue())
|
|
||||||
|
|
||||||
// Wait for HelmRepository to be Ready
|
secret := &corev1.Secret{
|
||||||
g.Eventually(func() bool {
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
if err := testEnv.Get(ctx, key, obj); err != nil {
|
GenerateName: "helmrepository-",
|
||||||
return false
|
Namespace: ns.Name,
|
||||||
}
|
},
|
||||||
if !conditions.IsReady(obj) {
|
Data: tt.secretData,
|
||||||
return false
|
}
|
||||||
}
|
if tt.secretType != "" {
|
||||||
readyCondition := conditions.Get(obj, meta.ReadyCondition)
|
secret.Type = tt.secretType
|
||||||
return obj.Generation == readyCondition.ObservedGeneration &&
|
}
|
||||||
obj.Generation == obj.Status.ObservedGeneration
|
|
||||||
}, timeout).Should(BeTrue())
|
|
||||||
|
|
||||||
// Check if the object status is valid.
|
g.Expect(testEnv.CreateAndWait(ctx, secret)).To(Succeed())
|
||||||
condns := &status.Conditions{NegativePolarity: helmRepositoryReadyCondition.NegativePolarity}
|
|
||||||
checker := status.NewChecker(testEnv.Client, condns)
|
|
||||||
checker.CheckErr(ctx, obj)
|
|
||||||
|
|
||||||
// kstatus client conformance check.
|
obj := &sourcev1.HelmRepository{
|
||||||
u, err := patch.ToUnstructured(obj)
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
g.Expect(err).ToNot(HaveOccurred())
|
GenerateName: "helmrepository-oci-reconcile-",
|
||||||
res, err := kstatus.Compute(u)
|
Namespace: ns.Name,
|
||||||
g.Expect(err).ToNot(HaveOccurred())
|
},
|
||||||
g.Expect(res.Status).To(Equal(kstatus.CurrentStatus))
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
|
Interval: metav1.Duration{Duration: interval},
|
||||||
|
URL: fmt.Sprintf("oci://%s", testRegistryserver.DockerRegistryHost),
|
||||||
|
SecretRef: &meta.LocalObjectReference{
|
||||||
|
Name: secret.Name,
|
||||||
|
},
|
||||||
|
Type: sourcev1.HelmRepositoryTypeOCI,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
g.Expect(testEnv.Create(ctx, obj)).To(Succeed())
|
||||||
|
|
||||||
// Patch the object with reconcile request annotation.
|
key := client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}
|
||||||
patchHelper, err := patch.NewHelper(obj, testEnv.Client)
|
|
||||||
g.Expect(err).ToNot(HaveOccurred())
|
// Wait for finalizer to be set
|
||||||
annotations := map[string]string{
|
g.Eventually(func() bool {
|
||||||
meta.ReconcileRequestAnnotation: "now",
|
if err := testEnv.Get(ctx, key, obj); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(obj.Finalizers) > 0
|
||||||
|
}, timeout).Should(BeTrue())
|
||||||
|
|
||||||
|
// Wait for HelmRepository to be Ready
|
||||||
|
g.Eventually(func() bool {
|
||||||
|
if err := testEnv.Get(ctx, key, obj); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !conditions.IsReady(obj) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
readyCondition := conditions.Get(obj, meta.ReadyCondition)
|
||||||
|
return obj.Generation == readyCondition.ObservedGeneration &&
|
||||||
|
obj.Generation == obj.Status.ObservedGeneration
|
||||||
|
}, timeout).Should(BeTrue())
|
||||||
|
|
||||||
|
// Check if the object status is valid.
|
||||||
|
condns := &status.Conditions{NegativePolarity: helmRepositoryReadyCondition.NegativePolarity}
|
||||||
|
checker := status.NewChecker(testEnv.Client, condns)
|
||||||
|
checker.CheckErr(ctx, obj)
|
||||||
|
|
||||||
|
// kstatus client conformance check.
|
||||||
|
u, err := patch.ToUnstructured(obj)
|
||||||
|
g.Expect(err).ToNot(HaveOccurred())
|
||||||
|
res, err := kstatus.Compute(u)
|
||||||
|
g.Expect(err).ToNot(HaveOccurred())
|
||||||
|
g.Expect(res.Status).To(Equal(kstatus.CurrentStatus))
|
||||||
|
|
||||||
|
// Patch the object with reconcile request annotation.
|
||||||
|
patchHelper, err := patch.NewHelper(obj, testEnv.Client)
|
||||||
|
g.Expect(err).ToNot(HaveOccurred())
|
||||||
|
annotations := map[string]string{
|
||||||
|
meta.ReconcileRequestAnnotation: "now",
|
||||||
|
}
|
||||||
|
obj.SetAnnotations(annotations)
|
||||||
|
g.Expect(patchHelper.Patch(ctx, obj)).ToNot(HaveOccurred())
|
||||||
|
g.Eventually(func() bool {
|
||||||
|
if err := testEnv.Get(ctx, key, obj); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return obj.Status.LastHandledReconcileAt == "now"
|
||||||
|
}, timeout).Should(BeTrue())
|
||||||
|
|
||||||
|
g.Expect(testEnv.Delete(ctx, obj)).To(Succeed())
|
||||||
|
|
||||||
|
// Wait for HelmRepository to be deleted
|
||||||
|
g.Eventually(func() bool {
|
||||||
|
if err := testEnv.Get(ctx, key, obj); err != nil {
|
||||||
|
return apierrors.IsNotFound(err)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, timeout).Should(BeTrue())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
obj.SetAnnotations(annotations)
|
|
||||||
g.Expect(patchHelper.Patch(ctx, obj)).ToNot(HaveOccurred())
|
|
||||||
g.Eventually(func() bool {
|
|
||||||
if err := testEnv.Get(ctx, key, obj); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return obj.Status.LastHandledReconcileAt == "now"
|
|
||||||
}, timeout).Should(BeTrue())
|
|
||||||
|
|
||||||
g.Expect(testEnv.Delete(ctx, obj)).To(Succeed())
|
|
||||||
|
|
||||||
// Wait for HelmRepository to be deleted
|
|
||||||
g.Eventually(func() bool {
|
|
||||||
if err := testEnv.Get(ctx, key, obj); err != nil {
|
|
||||||
return apierrors.IsNotFound(err)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}, timeout).Should(BeTrue())
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"helm.sh/helm/v3/pkg/getter"
|
"helm.sh/helm/v3/pkg/getter"
|
||||||
"helm.sh/helm/v3/pkg/registry"
|
helmreg "helm.sh/helm/v3/pkg/registry"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
|
@ -49,7 +49,7 @@ import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
"github.com/fluxcd/source-controller/internal/cache"
|
"github.com/fluxcd/source-controller/internal/cache"
|
||||||
"github.com/fluxcd/source-controller/internal/features"
|
"github.com/fluxcd/source-controller/internal/features"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/util"
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
// +kubebuilder:scaffold:imports
|
// +kubebuilder:scaffold:imports
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testRegistryClient *registry.Client
|
testRegistryClient *helmreg.Client
|
||||||
testRegistryserver *RegistryClientTestServer
|
testRegistryserver *RegistryClientTestServer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ type RegistryClientTestServer struct {
|
||||||
Out io.Writer
|
Out io.Writer
|
||||||
DockerRegistryHost string
|
DockerRegistryHost string
|
||||||
WorkspaceDir string
|
WorkspaceDir string
|
||||||
RegistryClient *registry.Client
|
RegistryClient *helmreg.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupServer(server *RegistryClientTestServer) string {
|
func SetupServer(server *RegistryClientTestServer) string {
|
||||||
|
|
@ -129,9 +129,9 @@ func SetupServer(server *RegistryClientTestServer) string {
|
||||||
server.Out = &out
|
server.Out = &out
|
||||||
|
|
||||||
// init test client
|
// init test client
|
||||||
server.RegistryClient, err = registry.NewClient(
|
server.RegistryClient, err = helmreg.NewClient(
|
||||||
registry.ClientOptDebug(true),
|
helmreg.ClientOptDebug(true),
|
||||||
registry.ClientOptWriter(server.Out),
|
helmreg.ClientOptWriter(server.Out),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("failed to create registry client: %s", err))
|
panic(fmt.Sprintf("failed to create registry client: %s", err))
|
||||||
|
|
@ -202,7 +202,7 @@ func TestMain(m *testing.M) {
|
||||||
testRegistryserver = &RegistryClientTestServer{}
|
testRegistryserver = &RegistryClientTestServer{}
|
||||||
registryWorkspaceDir := SetupServer(testRegistryserver)
|
registryWorkspaceDir := SetupServer(testRegistryserver)
|
||||||
|
|
||||||
testRegistryClient, err = registry.NewClient(registry.ClientOptWriter(os.Stdout))
|
testRegistryClient, err = helmreg.NewClient(helmreg.ClientOptWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Failed to create OCI registry client"))
|
panic(fmt.Sprintf("Failed to create OCI registry client"))
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +241,7 @@ func TestMain(m *testing.M) {
|
||||||
EventRecorder: record.NewFakeRecorder(32),
|
EventRecorder: record.NewFakeRecorder(32),
|
||||||
Metrics: testMetricsH,
|
Metrics: testMetricsH,
|
||||||
Getters: testGetters,
|
Getters: testGetters,
|
||||||
RegistryClientGenerator: util.RegistryClientGenerator,
|
RegistryClientGenerator: registry.ClientGenerator,
|
||||||
}).SetupWithManager(testEnv); err != nil {
|
}).SetupWithManager(testEnv); err != nil {
|
||||||
panic(fmt.Sprintf("Failed to start HelmRepositoryOCIReconciler: %v", err))
|
panic(fmt.Sprintf("Failed to start HelmRepositoryOCIReconciler: %v", err))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -18,6 +18,7 @@ require (
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3
|
github.com/cyphar/filepath-securejoin v0.2.3
|
||||||
github.com/darkowlzz/controller-check v0.0.0-20220325122359-11f5827b7981
|
github.com/darkowlzz/controller-check v0.0.0-20220325122359-11f5827b7981
|
||||||
github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684
|
github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684
|
||||||
|
github.com/docker/cli v20.10.11+incompatible
|
||||||
github.com/docker/go-units v0.4.0
|
github.com/docker/go-units v0.4.0
|
||||||
github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94
|
github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94
|
||||||
github.com/fluxcd/gitkit v0.5.0
|
github.com/fluxcd/gitkit v0.5.0
|
||||||
|
|
@ -101,7 +102,6 @@ require (
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||||
github.com/containerd/containerd v1.6.1 // indirect
|
github.com/containerd/containerd v1.6.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/cli v20.10.11+incompatible // indirect
|
|
||||||
github.com/docker/distribution v2.8.0+incompatible // indirect
|
github.com/docker/distribution v2.8.0+incompatible // indirect
|
||||||
github.com/docker/docker v20.10.12+incompatible // indirect
|
github.com/docker/docker v20.10.12+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
|
|
|
||||||
|
|
@ -199,9 +199,9 @@ func (b *remoteChartBuilder) downloadFromRepository(remote *repository.ChartRepo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
*buildResult = *result
|
||||||
|
|
||||||
if shouldReturn {
|
if shouldReturn {
|
||||||
*buildResult = *result
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,11 +212,11 @@ func (b *remoteChartBuilder) downloadFromRepository(remote *repository.ChartRepo
|
||||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
*buildResult = *result
|
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateBuildResult returns a Build object generated from the given chart version and build options. It also returns
|
||||||
|
// true if the given chart can be retrieved from cache and doesn't need to be downloaded again.
|
||||||
func generateBuildResult(cv *repo.ChartVersion, opts BuildOptions) (*Build, bool, error) {
|
func generateBuildResult(cv *repo.ChartVersion, opts BuildOptions) (*Build, bool, error) {
|
||||||
result := &Build{}
|
result := &Build{}
|
||||||
result.Version = cv.Version
|
result.Version = cv.Version
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config"
|
||||||
|
"helm.sh/helm/v3/pkg/registry"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoginOptionFromSecret derives authentication data from a Secret to login to an OCI registry. This Secret
|
||||||
|
// may either hold "username" and "password" fields or be of the corev1.SecretTypeDockerConfigJson type and hold
|
||||||
|
// a corev1.DockerConfigJsonKey field with a complete Docker configuration. If both, "username" and "password" are
|
||||||
|
// empty, a nil LoginOption and a nil error will be returned.
|
||||||
|
func LoginOptionFromSecret(registryURL string, secret corev1.Secret) (registry.LoginOption, error) {
|
||||||
|
var username, password string
|
||||||
|
if secret.Type == corev1.SecretTypeDockerConfigJson {
|
||||||
|
dockerCfg, err := config.LoadFromReader(bytes.NewReader(secret.Data[corev1.DockerConfigJsonKey]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load Docker config from Secret '%s': %w", secret.Name, err)
|
||||||
|
}
|
||||||
|
parsedURL, err := url.Parse(registryURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse registry URL '%s' while reconciling Secret '%s': %w",
|
||||||
|
registryURL, secret.Name, err)
|
||||||
|
}
|
||||||
|
authConfig, err := dockerCfg.GetAuthConfig(parsedURL.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get authentication data from Secret '%s': %w", secret.Name, err)
|
||||||
|
}
|
||||||
|
username = authConfig.Username
|
||||||
|
password = authConfig.Password
|
||||||
|
} else {
|
||||||
|
username, password = string(secret.Data["username"]), string(secret.Data["password"])
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case username == "" && password == "":
|
||||||
|
return nil, nil
|
||||||
|
case username == "" || password == "":
|
||||||
|
return nil, fmt.Errorf("invalid '%s' secret data: required fields 'username' and 'password'", secret.Name)
|
||||||
|
}
|
||||||
|
return registry.LoginOptBasicAuth(username, password), nil
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package util
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -23,10 +23,10 @@ import (
|
||||||
"helm.sh/helm/v3/pkg/registry"
|
"helm.sh/helm/v3/pkg/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegistryClientGenerator generates a registry client and a temporary credential file.
|
// ClientGenerator generates a registry client and a temporary credential file.
|
||||||
// The client is meant to be used for a single reconciliation.
|
// The client is meant to be used for a single reconciliation.
|
||||||
// The file is meant to be used for a single reconciliation and deleted after.
|
// The file is meant to be used for a single reconciliation and deleted after.
|
||||||
func RegistryClientGenerator(isLogin bool) (*registry.Client, string, error) {
|
func ClientGenerator(isLogin bool) (*registry.Client, string, error) {
|
||||||
if isLogin {
|
if isLogin {
|
||||||
// create a temporary file to store the credentials
|
// create a temporary file to store the credentials
|
||||||
// this is needed because otherwise the credentials are stored in ~/.docker/config.json.
|
// this is needed because otherwise the credentials are stored in ~/.docker/config.json.
|
||||||
10
main.go
10
main.go
|
|
@ -42,7 +42,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/internal/features"
|
"github.com/fluxcd/source-controller/internal/features"
|
||||||
"github.com/fluxcd/source-controller/internal/helm/util"
|
"github.com/fluxcd/source-controller/internal/helm/registry"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
"github.com/fluxcd/source-controller/controllers"
|
"github.com/fluxcd/source-controller/controllers"
|
||||||
|
|
@ -229,7 +229,7 @@ func main() {
|
||||||
MaxConcurrentReconciles: concurrent,
|
MaxConcurrentReconciles: concurrent,
|
||||||
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", sourcev1.HelmRepositoryKind)
|
setupLog.Error(err, "unable to create controller", "controller", sourcev1.HelmRepositoryKind, "type", "default")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,12 +239,12 @@ func main() {
|
||||||
Metrics: metricsH,
|
Metrics: metricsH,
|
||||||
Getters: getters,
|
Getters: getters,
|
||||||
ControllerName: controllerName,
|
ControllerName: controllerName,
|
||||||
RegistryClientGenerator: util.RegistryClientGenerator,
|
RegistryClientGenerator: registry.ClientGenerator,
|
||||||
}).SetupWithManagerAndOptions(mgr, controllers.HelmRepositoryReconcilerOptions{
|
}).SetupWithManagerAndOptions(mgr, controllers.HelmRepositoryReconcilerOptions{
|
||||||
MaxConcurrentReconciles: concurrent,
|
MaxConcurrentReconciles: concurrent,
|
||||||
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", sourcev1.HelmRepositoryKind)
|
setupLog.Error(err, "unable to create controller", "controller", sourcev1.HelmRepositoryKind, "type", "OCI")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +270,7 @@ func main() {
|
||||||
|
|
||||||
if err = (&controllers.HelmChartReconciler{
|
if err = (&controllers.HelmChartReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
RegistryClientGenerator: util.RegistryClientGenerator,
|
RegistryClientGenerator: registry.ClientGenerator,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Getters: getters,
|
Getters: getters,
|
||||||
EventRecorder: eventRecorder,
|
EventRecorder: eventRecorder,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue