Skip to content

Commit 778f3d3

Browse files
authored
[v0.14] Align Helm deployer client construction (#5046)
* 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 7b8eb81 commit 778f3d3

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
@@ -84,7 +84,7 @@ func (h *Helm) Setup(ctx context.Context, client client.Client, getter genericcl
8484
h.client = client
8585
h.getter = getter
8686

87-
cfg, err := h.createCfg(ctx, "")
87+
cfg, err := h.createCfg(ctx, "", h.getter)
8888
if err != nil {
8989
return err
9090
}
@@ -141,7 +141,7 @@ func (h *Helm) getCfg(ctx context.Context, namespace, serviceAccountName string)
141141
kClient := kube.New(getter)
142142
kClient.Namespace = namespace
143143

144-
cfg, err = h.createCfg(ctx, namespace)
144+
cfg, err = h.createCfg(ctx, namespace, getter)
145145
cfg.Releases.MaxHistory = MaxHelmHistory
146146
cfg.KubeClient = kClient
147147

@@ -150,12 +150,12 @@ func (h *Helm) getCfg(ctx context.Context, namespace, serviceAccountName string)
150150
return cfg, err
151151
}
152152

153-
func (h *Helm) createCfg(ctx context.Context, namespace string) (action.Configuration, error) {
153+
func (h *Helm) createCfg(ctx context.Context, namespace string, getter genericclioptions.RESTClientGetter) (action.Configuration, error) {
154154
logger := log.FromContext(ctx).WithName("helmSDK")
155155
info := func(format string, v ...interface{}) {
156156
logger.V(1).Info(fmt.Sprintf(format, v...))
157157
}
158-
kc := kube.New(h.getter)
158+
kc := kube.New(getter)
159159
kc.Log = info
160160
clientSet, err := kc.Factory.KubernetesClientSet()
161161
if err != nil {
@@ -167,7 +167,7 @@ func (h *Helm) createCfg(ctx context.Context, namespace string) (action.Configur
167167
store.MaxHistory = MaxHelmHistory
168168

169169
return action.Configuration{
170-
RESTClientGetter: h.getter,
170+
RESTClientGetter: getter,
171171
Releases: store,
172172
KubeClient: kc,
173173
Log: info,

internal/helmdeployer/install.go

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

2323
corev1 "k8s.io/api/core/v1"
24-
"k8s.io/apimachinery/pkg/types"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2525
"k8s.io/apimachinery/pkg/util/yaml"
26+
"k8s.io/client-go/kubernetes"
27+
"k8s.io/client-go/rest"
2628
"sigs.k8s.io/controller-runtime/pkg/log"
2729
)
2830

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

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

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

322-
func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOptions, defaultNamespace string) (map[string]interface{}, error) {
334+
func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOptions, defaultNamespace string, kubeClient kubernetes.Interface) (map[string]interface{}, error) {
323335
if options.Helm == nil {
324336
return nil, nil
325337
}
@@ -333,8 +345,8 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
333345
if values == nil {
334346
values = map[string]interface{}{}
335347
}
336-
// do not run this when using template
337-
if !h.template {
348+
// kubeClient is nil in template mode; skip the cluster lookups in that case.
349+
if kubeClient != nil {
338350
for _, valuesFrom := range options.Helm.ValuesFrom {
339351
var tempValues map[string]interface{}
340352
if valuesFrom.ConfigMapKeyRef != nil {
@@ -349,8 +361,7 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
349361
if key == "" {
350362
key = DefaultKey
351363
}
352-
configMap := &corev1.ConfigMap{}
353-
err := h.client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, configMap)
364+
configMap, err := kubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{})
354365
if err != nil {
355366
return nil, err
356367
}
@@ -377,8 +388,7 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
377388
if key == "" {
378389
key = DefaultKey
379390
}
380-
secret := &corev1.Secret{}
381-
err := h.client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, secret)
391+
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
382392
if err != nil {
383393
return nil, err
384394
}
@@ -396,6 +406,21 @@ func (h *Helm) getValues(ctx context.Context, options fleet.BundleDeploymentOpti
396406
return values, nil
397407
}
398408

409+
// restConfigGetter produces a *rest.Config. Both
410+
// genericclioptions.RESTClientGetter and action.RESTClientGetter satisfy it.
411+
type restConfigGetter interface {
412+
ToRESTConfig() (*rest.Config, error)
413+
}
414+
415+
func kubeClientFromGetter(getter restConfigGetter) (kubernetes.Interface, error) {
416+
restConfig, err := getter.ToRESTConfig()
417+
if err != nil {
418+
return nil, err
419+
}
420+
421+
return kubernetes.NewForConfig(restConfig)
422+
}
423+
399424
func valuesFromSecret(name, namespace, key string, secret *corev1.Secret) (map[string]interface{}, error) {
400425
var m map[string]interface{}
401426
if secret == nil {

internal/helmdeployer/install_test.go

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import (
1616
corev1 "k8s.io/api/core/v1"
1717
apierrors "k8s.io/apimachinery/pkg/api/errors"
1818
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19-
kruntime "k8s.io/apimachinery/pkg/runtime"
20-
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
21-
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
22-
"sigs.k8s.io/controller-runtime/pkg/client/fake"
19+
kubernetesfake "k8s.io/client-go/kubernetes/fake"
2320
)
2421

2522
func TestValuesFrom(t *testing.T) {
@@ -171,9 +168,6 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
171168
a := assert.New(t)
172169
r := require.New(t)
173170

174-
scheme := kruntime.NewScheme()
175-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
176-
177171
// default namespace where the Helm release lives
178172
defaultNS := "helm-default"
179173

@@ -189,9 +183,8 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
189183
Data: map[string][]byte{DefaultKey: []byte("secVal: secDefault")},
190184
}
191185

192-
cl := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cm, &sec).Build()
193-
194-
h := &Helm{client: cl, template: false}
186+
kubeClient := kubernetesfake.NewSimpleClientset(&cm, &sec)
187+
h := &Helm{template: false}
195188

196189
opts := fleet.BundleDeploymentOptions{
197190
Helm: &fleet.HelmOptions{
@@ -208,7 +201,7 @@ func TestValuesFromUsesDefaultNamespaceWhenResourceCopiedDownstream(t *testing.T
208201
os.Setenv(experimental.CopyResourcesDownstreamFlag, "true")
209202
defer os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
210203

211-
vals, err := h.getValues(context.TODO(), opts, defaultNS)
204+
vals, err := h.getValues(context.TODO(), opts, defaultNS, kubeClient)
212205
r.NoError(err)
213206

214207
// configmap and secret data should have been read from defaultNS
@@ -220,9 +213,6 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
220213
a := assert.New(t)
221214
r := require.New(t)
222215

223-
scheme := kruntime.NewScheme()
224-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
225-
226216
// namespaces
227217
defaultNS := "helm-default"
228218
providedNS := "explicit-ns"
@@ -233,8 +223,8 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
233223
Data: map[string]string{DefaultKey: "cmVal: cmProvided"},
234224
}
235225

236-
cl := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cmProvided).Build()
237-
h := &Helm{client: cl, template: false}
226+
kubeClient := kubernetesfake.NewSimpleClientset(&cmProvided)
227+
h := &Helm{template: false}
238228

239229
opts := fleet.BundleDeploymentOptions{
240230
Helm: &fleet.HelmOptions{
@@ -248,7 +238,7 @@ func TestValuesFromUsesProvidedNamespaceWhenNotCopiedDownstream(t *testing.T) {
248238
os.Setenv(experimental.CopyResourcesDownstreamFlag, "true")
249239
defer os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
250240

251-
vals, err := h.getValues(context.TODO(), opts, defaultNS)
241+
vals, err := h.getValues(context.TODO(), opts, defaultNS, kubeClient)
252242
r.NoError(err)
253243
a.Equal("cmProvided", vals["cmVal"])
254244
}
@@ -257,11 +247,8 @@ func TestValuesFromErrorWhenCopiedDownstreamButExperimentalDisabled(t *testing.T
257247
a := assert.New(t)
258248
r := require.New(t)
259249

260-
scheme := kruntime.NewScheme()
261-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
262-
263-
cl := fake.NewClientBuilder().WithScheme(scheme).Build()
264-
h := &Helm{client: cl, template: false}
250+
kubeClient := kubernetesfake.NewSimpleClientset()
251+
h := &Helm{template: false}
265252

266253
opts := fleet.BundleDeploymentOptions{
267254
Helm: &fleet.HelmOptions{
@@ -276,7 +263,7 @@ func TestValuesFromErrorWhenCopiedDownstreamButExperimentalDisabled(t *testing.T
276263
// ensure experimental feature is disabled
277264
os.Unsetenv(experimental.CopyResourcesDownstreamFlag)
278265

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

0 commit comments

Comments
 (0)