Skip to content

Commit 7e2d0c3

Browse files
author
Miguel Sacristan
committed
Initial implementation of Helm Renderer
1 parent 0a3254e commit 7e2d0c3

File tree

2 files changed

+132
-20
lines changed

2 files changed

+132
-20
lines changed

pkg/skaffold/deploy/helm.go

+88-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"bufio"
2121
"bytes"
2222
"context"
23-
"errors"
2423
"fmt"
2524
"io"
2625
"io/ioutil"
@@ -51,7 +50,7 @@ import (
5150

5251
var (
5352
// versionRegex extracts version from "helm version --client", for instance: "2.14.0-rc.2"
54-
versionRegex = regexp.MustCompile(`\"v(\d[\w\.\-\.]+)`)
53+
versionRegex = regexp.MustCompile(`v(\d[\w.\-]+)`)
5554

5655
// helm3Version represents the version cut-off for helm3 behavior
5756
helm3Version = semver.MustParse("3.0.0-beta.0")
@@ -127,10 +126,10 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build
127126

128127
// Let's make sure that every image tag is set with `--set`.
129128
// Otherwise, templates have no way to use the images that were built.
130-
for _, build := range builds {
131-
if !valuesSet[build.Tag] {
132-
warnings.Printf("image [%s] is not used.", build.Tag)
133-
warnings.Printf("image [%s] is used instead.", build.ImageName)
129+
for _, b := range builds {
130+
if !valuesSet[b.Tag] {
131+
warnings.Printf("image [%s] is not used.", b.Tag)
132+
warnings.Printf("image [%s] is used instead.", b.ImageName)
134133
warnings.Printf("See helm sample for how to replace image names with their actual tags: https://github.com/GoogleContainerTools/skaffold/blob/master/examples/helm-deployment/skaffold.yaml")
135134
}
136135
}
@@ -214,8 +213,87 @@ func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
214213
}
215214

216215
// Render generates the Kubernetes manifests and writes them out
217-
func (h *HelmDeployer) Render(context.Context, io.Writer, []build.Artifact, []Labeller, string) error {
218-
return errors.New("not yet implemented")
216+
func (h *HelmDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller, filepath string) error {
217+
hv, err := h.binVer(ctx)
218+
if err != nil {
219+
return fmt.Errorf("binary version: %w", err)
220+
}
221+
222+
var output io.Writer
223+
224+
if filepath != "" {
225+
f, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, 0666)
226+
if err != nil {
227+
return fmt.Errorf("opening file for writing manifests: %w", err)
228+
}
229+
defer f.Close()
230+
231+
output = f
232+
} else {
233+
output = out
234+
}
235+
236+
for _, r := range h.Releases {
237+
238+
args := []string{"template", r.ChartPath}
239+
240+
if hv.GTE(helm3Version) {
241+
// Helm 3 requires the name to be before the chart path
242+
args = append(args[:1], append([]string{r.Name}, args[1:]...)...)
243+
} else {
244+
args = append(args, "--name", r.Name)
245+
}
246+
247+
for _, vf := range r.ValuesFiles {
248+
args = append(args, "--values", vf)
249+
}
250+
251+
params, err := pairParamsToArtifacts(builds, r.Values)
252+
if err != nil {
253+
return fmt.Errorf("matching build results to chart values: %w", err)
254+
}
255+
256+
for k, v := range params {
257+
var value string
258+
259+
cfg := r.ImageStrategy.HelmImageConfig.HelmConventionConfig
260+
261+
value, err = imageSetFromConfig(cfg, k, v.Tag)
262+
if err != nil {
263+
return err
264+
}
265+
266+
args = append(args, "--set-string", value)
267+
}
268+
269+
for key, value := range r.Values {
270+
args = append(args, "--set", fmt.Sprintf("%s=%s", key, value))
271+
}
272+
273+
sortedKeys := make([]string, 0, len(r.SetValueTemplates))
274+
for k := range r.SetValueTemplates {
275+
sortedKeys = append(sortedKeys, k)
276+
}
277+
sort.Strings(sortedKeys)
278+
279+
for _, key := range sortedKeys {
280+
v, err := util.ExpandEnvTemplate(r.SetValueTemplates[key], nil)
281+
if err != nil {
282+
return err
283+
}
284+
args = append(args, "--set", fmt.Sprintf("%s=%s", key, v))
285+
}
286+
287+
if r.Namespace != "" {
288+
args = append(args, "--namespace", r.Namespace)
289+
}
290+
291+
if err := h.exec(ctx, output, false, args...); err != nil {
292+
return err
293+
}
294+
}
295+
296+
return nil
219297
}
220298

221299
// exec executes the helm command, writing combined stdout/stderr to the provided writer
@@ -607,11 +685,9 @@ func pairParamsToArtifacts(builds []build.Artifact, params map[string]string) (m
607685

608686
for param, imageName := range params {
609687
b, ok := imageToBuildResult[imageName]
610-
if !ok {
611-
return nil, fmt.Errorf("no build present for %s", imageName)
688+
if ok {
689+
paramToBuildResult[param] = b
612690
}
613-
614-
paramToBuildResult[param] = b
615691
}
616692

617693
return paramToBuildResult, nil

pkg/skaffold/deploy/helm_test.go

+44-8
Original file line numberDiff line numberDiff line change
@@ -486,16 +486,21 @@ func TestHelmDeploy(t *testing.T) {
486486
builds: testBuilds,
487487
},
488488
{
489-
description: "deploy should error for unmatched parameter",
489+
description: "deploy should warn for unmatched parameter",
490490
commands: testutil.
491491
CmdRunWithOutput("helm version", version21).
492492
AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig").
493493
AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
494-
AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig").
494+
AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --kubeconfig kubeconfig").
495495
AndRun("helm --kube-context kubecontext get skaffold-helm --kubeconfig kubeconfig"),
496496
runContext: makeRunContext(testDeployConfigParameterUnmatched, false),
497497
builds: testBuilds,
498-
shouldErr: true,
498+
shouldErr: false,
499+
expectedWarnings: []string{
500+
"See helm sample for how to replace image names with their actual tags: https://github.com/GoogleContainerTools/skaffold/blob/master/examples/helm-deployment/skaffold.yaml",
501+
"image [docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184] is not used.",
502+
"image [skaffold-helm] is used instead.",
503+
},
499504
},
500505
{
501506
description: "deploy success remote chart with skipBuildDependencies",
@@ -975,17 +980,48 @@ func TestHelmRender(t *testing.T) {
975980
tests := []struct {
976981
description string
977982
shouldErr bool
983+
commands util.Command
984+
runContext *runcontext.RunContext
978985
}{
979986
{
980-
description: "calling render returns error",
981-
shouldErr: true,
987+
description: "normal render v2",
988+
shouldErr: false,
989+
commands: testutil.
990+
CmdRunWithOutput("helm version", version21).
991+
AndRun("helm --kube-context kubecontext template examples/test --name skaffold-helm --set image=skaffold-helm --kubeconfig kubeconfig"),
992+
runContext: makeRunContext(testDeployConfig, false),
993+
},
994+
{
995+
description: "normal render v3",
996+
shouldErr: false,
997+
commands: testutil.
998+
CmdRunWithOutput("helm version", version31).
999+
AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set image=skaffold-helm --kubeconfig kubeconfig"),
1000+
runContext: makeRunContext(testDeployConfig, false),
1001+
},
1002+
{
1003+
description: "render with templated config",
1004+
shouldErr: false,
1005+
commands: testutil.
1006+
CmdRunWithOutput("helm version", version31).
1007+
AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set image=skaffold-helm --set image.name=<no value> --set image.tag=<no value> --set missing.key=<no value> --set other.key=<no value> --set some.key=somevalue --kubeconfig kubeconfig"),
1008+
runContext: makeRunContext(testDeployConfigTemplated, false),
1009+
},
1010+
{
1011+
description: "render with namespace",
1012+
shouldErr: false,
1013+
commands: testutil.CmdRunWithOutput("helm version", version31).
1014+
AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set image=skaffold-helm --namespace testNamespace --kubeconfig kubeconfig"),
1015+
runContext: makeRunContext(testDeployNamespacedConfig, false),
9821016
},
9831017
}
9841018
for _, test := range tests {
9851019
testutil.Run(t, test.description, func(t *testutil.T) {
986-
deployer := NewHelmDeployer(&runcontext.RunContext{})
987-
actual := deployer.Render(context.Background(), ioutil.Discard, []build.Artifact{}, nil, "tmp/dir")
988-
t.CheckError(test.shouldErr, actual)
1020+
deployer := NewHelmDeployer(test.runContext)
1021+
1022+
t.Override(&util.DefaultExecCommand, test.commands)
1023+
err := deployer.Render(context.Background(), ioutil.Discard, []build.Artifact{}, nil, "")
1024+
t.CheckError(test.shouldErr, err)
9891025
})
9901026
}
9911027
}

0 commit comments

Comments
 (0)