Skip to content

Commit 190f281

Browse files
authored
[v0.15] Align Helm deployer client construction (#5044)
* Use consistent getter in Helm configuration createCfg used the base getter for both RESTClientGetter and KubeClient independently of the getter produced in getCfg. Accept a getter parameter in createCfg so callers control which getter is used for all client construction. * Use Helm configuration getter for valuesFrom client Construct the kubernetes.Interface used to resolve valuesFrom references from the Helm configuration's RESTClientGetter.
1 parent bed4ca0 commit 190f281

3 files changed

Lines changed: 50 additions & 38 deletions

File tree

internal/helmdeployer/deployer.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func (h *Helm) Setup(ctx context.Context, client client.Client, getter genericcl
8585
h.client = client
8686
h.getter = getter
8787

88-
cfg, err := h.createCfg(ctx, "")
88+
cfg, err := h.createCfg(ctx, "", h.getter)
8989
if err != nil {
9090
return err
9191
}
@@ -139,7 +139,7 @@ func (h *Helm) getCfg(ctx context.Context, namespace, serviceAccountName string)
139139
kClient := kube.New(getter)
140140
kClient.Namespace = namespace
141141

142-
cfg, err := h.createCfg(ctx, namespace)
142+
cfg, err := h.createCfg(ctx, namespace, getter)
143143
if err != nil {
144144
return nil, err
145145
}
@@ -151,7 +151,7 @@ func (h *Helm) getCfg(ctx context.Context, namespace, serviceAccountName string)
151151
return cfg, nil
152152
}
153153

154-
func (h *Helm) createCfg(ctx context.Context, namespace string) (*action.Configuration, error) {
154+
func (h *Helm) createCfg(ctx context.Context, namespace string, getter genericclioptions.RESTClientGetter) (*action.Configuration, error) {
155155
// Create a logger handler for Helm SDK components.
156156
// This uses Fleet's controller-runtime logger (which uses logr/zapr) and adapts it to slog.
157157
// The logger level is set to V(1) to match the verbosity level used in Helm v3.
@@ -160,7 +160,7 @@ func (h *Helm) createCfg(ctx context.Context, namespace string) (*action.Configu
160160
Level: slog.LevelDebug,
161161
})
162162

163-
kc := kube.New(h.getter)
163+
kc := kube.New(getter)
164164
kc.SetLogger(handler)
165165
clientSet, err := kc.Factory.KubernetesClientSet()
166166
if err != nil {
@@ -172,7 +172,7 @@ func (h *Helm) createCfg(ctx context.Context, namespace string) (*action.Configu
172172
store.MaxHistory = MaxHelmHistory
173173

174174
cfg := &action.Configuration{
175-
RESTClientGetter: h.getter,
175+
RESTClientGetter: getter,
176176
Releases: store,
177177
KubeClient: kc,
178178
}

internal/helmdeployer/install.go

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
fleet "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
2424

2525
corev1 "k8s.io/api/core/v1"
26-
"k8s.io/apimachinery/pkg/types"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727
"k8s.io/apimachinery/pkg/util/yaml"
28+
"k8s.io/client-go/kubernetes"
29+
"k8s.io/client-go/rest"
2830
"sigs.k8s.io/controller-runtime/pkg/log"
2931
)
3032

@@ -78,12 +80,22 @@ func (h *Helm) install(ctx context.Context, bundleID string, manifest *manifest.
7880
logger := log.FromContext(ctx).WithName("helm-deployer").WithName("install").WithValues("commit", manifest.Commit, "dryRun", dryRunCfg.DryRun)
7981
timeout, defaultNamespace, releaseName := h.getOpts(bundleID, options)
8082

81-
values, err := h.getValues(ctx, options, defaultNamespace)
83+
cfg, err := h.getCfg(ctx, defaultNamespace, options.ServiceAccount)
8284
if err != nil {
8385
return nil, err
8486
}
8587

86-
cfg, err := h.getCfg(ctx, defaultNamespace, options.ServiceAccount)
88+
// kubeClient is nil in template mode; getValues treats a nil client as
89+
// "skip ValuesFrom lookup" and returns only the statically defined values.
90+
var kubeClient kubernetes.Interface
91+
if !h.template {
92+
kubeClient, err = kubeClientFromGetter(cfg.RESTClientGetter)
93+
if err != nil {
94+
return nil, err
95+
}
96+
}
97+
98+
values, err := h.getValues(ctx, options, defaultNamespace, kubeClient)
8799
if err != nil {
88100
return nil, err
89101
}
@@ -466,7 +478,7 @@ func handleOrphanedRelease(ctx context.Context, cfg *action.Configuration, lastR
466478
return false, nil
467479
}
468480

469-
func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOptions, defaultNamespace string) (map[string]interface{}, error) {
481+
func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOptions, defaultNamespace string, kubeClient kubernetes.Interface) (map[string]interface{}, error) {
470482
if options.Helm == nil {
471483
return nil, nil
472484
}
@@ -480,8 +492,8 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
480492
if values == nil {
481493
values = map[string]interface{}{}
482494
}
483-
// do not run this when using template
484-
if !h.template {
495+
// kubeClient is nil in template mode; skip the cluster lookups in that case.
496+
if kubeClient != nil {
485497
for _, valuesFrom := range options.Helm.ValuesFrom {
486498
var tempValues map[string]interface{}
487499
if valuesFrom.ConfigMapKeyRef != nil {
@@ -496,8 +508,7 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
496508
if key == "" {
497509
key = DefaultKey
498510
}
499-
configMap := &corev1.ConfigMap{}
500-
err := h.client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, configMap)
511+
configMap, err := kubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{})
501512
if err != nil {
502513
return nil, err
503514
}
@@ -524,8 +535,7 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
524535
if key == "" {
525536
key = DefaultKey
526537
}
527-
secret := &corev1.Secret{}
528-
err := h.client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, secret)
538+
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
529539
if err != nil {
530540
return nil, err
531541
}
@@ -543,6 +553,21 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
543553
return values, nil
544554
}
545555

556+
// restConfigGetter produces a *rest.Config. Both
557+
// genericclioptions.RESTClientGetter and action.RESTClientGetter satisfy it.
558+
type restConfigGetter interface {
559+
ToRESTConfig() (*rest.Config, error)
560+
}
561+
562+
func kubeClientFromGetter(getter restConfigGetter) (kubernetes.Interface, error) {
563+
restConfig, err := getter.ToRESTConfig()
564+
if err != nil {
565+
return nil, err
566+
}
567+
568+
return kubernetes.NewForConfig(restConfig)
569+
}
570+
546571
func valuesFromSecret(name, namespace, key string, secret *corev1.Secret) (map[string]interface{}, error) {
547572
var m map[string]interface{}
548573
if secret == nil {

internal/helmdeployer/install_test.go

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ import (
1919
corev1 "k8s.io/api/core/v1"
2020
apierrors "k8s.io/apimachinery/pkg/api/errors"
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22-
kruntime "k8s.io/apimachinery/pkg/runtime"
23-
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
24-
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
25-
"sigs.k8s.io/controller-runtime/pkg/client/fake"
22+
kubernetesfake "k8s.io/client-go/kubernetes/fake"
2623
)
2724

2825
func TestValuesFrom(t *testing.T) {
@@ -560,9 +557,6 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
560557
a := assert.New(t)
561558
r := require.New(t)
562559

563-
scheme := kruntime.NewScheme()
564-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
565-
566560
// default namespace where the Helm release lives
567561
defaultNS := "helm-default"
568562

@@ -578,9 +572,8 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
578572
Data: map[string][]byte{DefaultKey: []byte("secVal: secDefault")},
579573
}
580574

581-
cl := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cm, &sec).Build()
582-
583-
h := &Helm{client: cl, template: false}
575+
kubeClient := kubernetesfake.NewSimpleClientset(&cm, &sec)
576+
h := &Helm{template: false}
584577

585578
opts := fleet.BundleDeploymentOptions{
586579
Helm: &fleet.HelmOptions{
@@ -597,7 +590,7 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
597590
os.Setenv(experimental.CopyResourcesDownstreamFlag, "true")
598591
defer os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
599592

600-
vals, err := h.getValues(context.TODO(), opts, defaultNS)
593+
vals, err := h.getValues(context.TODO(), opts, defaultNS, kubeClient)
601594
r.NoError(err)
602595

603596
// configmap and secret data should have been read from defaultNS
@@ -609,9 +602,6 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
609602
a := assert.New(t)
610603
r := require.New(t)
611604

612-
scheme := kruntime.NewScheme()
613-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
614-
615605
// namespaces
616606
defaultNS := "helm-default"
617607
providedNS := "explicit-ns"
@@ -622,8 +612,8 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
622612
Data: map[string]string{DefaultKey: "cmVal: cmProvided"},
623613
}
624614

625-
cl := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cmProvided).Build()
626-
h := &Helm{client: cl, template: false}
615+
kubeClient := kubernetesfake.NewSimpleClientset(&cmProvided)
616+
h := &Helm{template: false}
627617

628618
opts := fleet.BundleDeploymentOptions{
629619
Helm: &fleet.HelmOptions{
@@ -637,7 +627,7 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
637627
os.Setenv(experimental.CopyResourcesDownstreamFlag, "true")
638628
defer os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
639629

640-
vals, err := h.getValues(context.TODO(), opts, defaultNS)
630+
vals, err := h.getValues(context.TODO(), opts, defaultNS, kubeClient)
641631
r.NoError(err)
642632
a.Equal("cmProvided", vals["cmVal"])
643633
}
@@ -646,11 +636,8 @@ func TestValuesFromErrorWhenCopiedDownstreamButExperimentalDisabled(t *testing.T
646636
a := assert.New(t)
647637
r := require.New(t)
648638

649-
scheme := kruntime.NewScheme()
650-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
651-
652-
cl := fake.NewClientBuilder().WithScheme(scheme).Build()
653-
h := &Helm{client: cl, template: false}
639+
kubeClient := kubernetesfake.NewSimpleClientset()
640+
h := &Helm{template: false}
654641

655642
opts := fleet.BundleDeploymentOptions{
656643
Helm: &fleet.HelmOptions{
@@ -665,7 +652,7 @@ func TestValuesFromErrorWhenCopiedDownstreamButExperimentalDisabled(t *testing.T
665652
// ensure experimental feature is disabled
666653
os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
667654

668-
_, err := h.getValues(context.TODO(), opts, "default-ns")
655+
_, err := h.getValues(context.TODO(), opts, "default-ns", kubeClient)
669656
r.Error(err)
670657
// get will fail trying to read from provided-ns and should report not found
671658
a.True(apierrors.IsNotFound(err), "expected a NotFound error when valuesFrom references resources and experimental feature is disabled")

0 commit comments

Comments
 (0)