Skip to content

Commit e1e4fc3

Browse files
authored
feat(contrib): select helm version (#5058) (#5066)
Signed-off-by: Axel Dumortier <[email protected]>
1 parent 06bdf35 commit e1e4fc3

File tree

4 files changed

+185
-19
lines changed

4 files changed

+185
-19
lines changed

contrib/integrations/kubernetes/kubernetes.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ deployment_default_config:
1515
timeout:
1616
type: string
1717
value: 180
18-
description: timeout in seconds
18+
description: "timeout in seconds for v2 or duration for v3 (ex: 3m)"
1919
namespace:
2020
type: string
2121
value: default
22-
description: Kubernetes namespace in which you want to deploy your components (OPTIONAL)
22+
description: "Kubernetes namespace in which you want to deploy your components (OPTIONAL)"
2323
deployment_files:
2424
type: string
2525
description: Glob to yaml filepaths
@@ -29,3 +29,6 @@ deployment_default_config:
2929
helm_values:
3030
type: string
3131
description: specify helm values in a YAML file or a URL to configure/override your helm chart
32+
helm_version:
33+
type: string
34+
description: "specify helm version to use (default: 2.12.2)"

contrib/integrations/kubernetes/plugin-kubernetes/main.go

+162-16
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import (
1414
"os/exec"
1515
"path"
1616
"path/filepath"
17+
"strconv"
1718
"strings"
1819
"time"
1920

21+
"github.com/blang/semver"
2022
"github.com/golang/protobuf/ptypes/empty"
2123
apiv1 "k8s.io/api/core/v1"
2224
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -277,24 +279,26 @@ func executeK8s(q *integrationplugin.DeployQuery) error {
277279
}
278280

279281
func executeHelm(q *integrationplugin.DeployQuery) error {
280-
releaseName := q.GetOptions()["cds.integration.release_name"]
281-
namespace := q.GetOptions()["cds.integration.namespace"]
282-
helmChart := q.GetOptions()["cds.integration.helm_chart"]
283-
helmValues := q.GetOptions()["cds.integration.helm_values"]
284-
timeoutStr := q.GetOptions()["cds.integration.timeout"]
285282
project := q.GetOptions()["cds.project"]
286283
workflow := q.GetOptions()["cds.workflow"]
287-
application := q.GetOptions()["cds.application"]
288-
if namespace == "" {
289-
namespace = "default"
284+
helmVersion := q.GetOptions()["cds.integration.helm_version"]
285+
286+
if helmVersion == "" {
287+
helmVersion = "2.12.2"
290288
}
291-
if releaseName != "" {
292-
application = releaseName
289+
290+
version, err := semver.Parse(helmVersion)
291+
if err != nil {
292+
return fmt.Errorf("Invalid Helm Version %s - err: %v", helmVersion, err)
293+
}
294+
supportedVersion := ">=2.0.0 <4.0.0"
295+
expectedRange, err := semver.ParseRange(supportedVersion)
296+
if err != nil {
297+
return fmt.Errorf("Fail to parse semver range : %v", err)
293298
}
294299

295-
helmFound := false
296-
if _, err := exec.LookPath("helm"); err == nil {
297-
helmFound = true
300+
if !expectedRange(version) {
301+
return fmt.Errorf("Unsupported helm version, should be : %s", supportedVersion)
298302
}
299303

300304
cwd, err := os.Getwd()
@@ -303,12 +307,38 @@ func executeHelm(q *integrationplugin.DeployQuery) error {
303307
}
304308

305309
binaryName := "helm"
310+
kubeCfg := "KUBECONFIG=" + path.Join(cwd, ".kube/config")
311+
312+
helmFound := false
313+
if _, err := exec.LookPath("helm"); err == nil {
314+
helmFound = true
315+
}
316+
317+
if helmFound {
318+
out, err := exec.Command(binaryName, "version", "--client", "--short").Output()
319+
if err != nil {
320+
return fmt.Errorf("Cannot check helm version : %v", err)
321+
}
322+
installedHelm := strings.TrimPrefix(string(out), "Client: ")
323+
installedVersion, err := semver.ParseTolerant(installedHelm)
324+
if err != nil {
325+
return fmt.Errorf("Invalid installed Helm Version %s - err: %v", installedHelm, err)
326+
}
327+
328+
if !version.Equals(installedVersion) {
329+
fmt.Println("Helm in path is not at correct version, need installation")
330+
fmt.Printf("Path version : %s\n", installedVersion.String())
331+
fmt.Printf("Target version : %s\n", version.String())
332+
helmFound = false
333+
}
334+
}
335+
306336
if !helmFound {
307-
fmt.Println("Download helm in progress...")
337+
fmt.Printf("Download helm %s in progress...\n", version.String())
308338
netClient := &http.Client{
309339
Timeout: time.Second * 600,
310340
}
311-
response, err := netClient.Get("https://storage.googleapis.com/kubernetes-helm/helm-v2.12.2-" + sdk.GOOS + "-" + sdk.GOARCH + ".tar.gz")
341+
response, err := netClient.Get(fmt.Sprintf("https://get.helm.sh/helm-v%s-%s-%s.tar.gz", version.String(), sdk.GOOS, sdk.GOARCH))
312342
if err != nil {
313343
return fmt.Errorf("Cannot download helm : %v", err)
314344
}
@@ -334,14 +364,43 @@ func executeHelm(q *integrationplugin.DeployQuery) error {
334364
binaryName = path.Join(".", binaryName, sdk.GOOS+"-"+sdk.GOARCH, "helm")
335365
}
336366

367+
switch version.Major {
368+
case 2:
369+
return executeHelmV2(binaryName, kubeCfg, q)
370+
case 3:
371+
return executeHelmV3(binaryName, kubeCfg, q)
372+
}
373+
374+
return fmt.Errorf("Unsupported helm version")
375+
}
376+
377+
func executeHelmV2(binaryName, kubeCfg string, q *integrationplugin.DeployQuery) error {
378+
releaseName := q.GetOptions()["cds.integration.release_name"]
379+
namespace := q.GetOptions()["cds.integration.namespace"]
380+
helmChart := q.GetOptions()["cds.integration.helm_chart"]
381+
helmValues := q.GetOptions()["cds.integration.helm_values"]
382+
timeoutStr := q.GetOptions()["cds.integration.timeout"]
383+
application := q.GetOptions()["cds.application"]
384+
385+
if namespace == "" {
386+
namespace = "default"
387+
}
388+
if releaseName != "" {
389+
application = releaseName
390+
}
391+
392+
if d, err := time.ParseDuration(timeoutStr); err == nil {
393+
timeoutStr = strconv.Itoa(int(d.Seconds()))
394+
fmt.Println("timeout is a duration, converting timeout in seconds to " + timeoutStr)
395+
}
396+
337397
cmdInit := exec.Command(binaryName, "init", "--client-only")
338398
cmdInit.Env = os.Environ()
339399
cmdInit.Stderr = os.Stderr
340400
cmdInit.Stdout = os.Stdout
341401
if err := cmdInit.Run(); err != nil {
342402
return fmt.Errorf("Cannot execute helm init : %v", err)
343403
}
344-
kubeCfg := "KUBECONFIG=" + path.Join(cwd, ".kube/config")
345404

346405
if _, err := os.Stat(helmChart); err == nil {
347406
fmt.Println("Helm dependency update")
@@ -402,6 +461,93 @@ func executeHelm(q *integrationplugin.DeployQuery) error {
402461
return nil
403462
}
404463

464+
func executeHelmV3(binaryName, kubeCfg string, q *integrationplugin.DeployQuery) error {
465+
releaseName := q.GetOptions()["cds.integration.release_name"]
466+
namespace := q.GetOptions()["cds.integration.namespace"]
467+
helmChart := q.GetOptions()["cds.integration.helm_chart"]
468+
helmValues := q.GetOptions()["cds.integration.helm_values"]
469+
timeoutStr := q.GetOptions()["cds.integration.timeout"]
470+
application := q.GetOptions()["cds.application"]
471+
472+
if namespace == "" {
473+
namespace = "default"
474+
}
475+
if releaseName != "" {
476+
application = releaseName
477+
}
478+
if _, err := time.ParseDuration(timeoutStr); err != nil {
479+
timeoutStr = timeoutStr + "s"
480+
fmt.Println("timeout is not a duration, setting timeout to " + timeoutStr)
481+
}
482+
483+
cmdRepoAdd := exec.Command(binaryName, "repo", "add", "stable", "https://kubernetes-charts.storage.googleapis.com/")
484+
cmdRepoAdd.Env = os.Environ()
485+
cmdRepoAdd.Stderr = os.Stderr
486+
cmdRepoAdd.Stdout = os.Stdout
487+
if err := cmdRepoAdd.Run(); err != nil {
488+
return fmt.Errorf("Cannot execute helm repo add stable : %v", err)
489+
}
490+
491+
if _, err := os.Stat(helmChart); err == nil {
492+
fmt.Println("Helm dependency update")
493+
cmdDependency := exec.Command(binaryName, "dependency", "update", helmChart)
494+
cmdDependency.Env = os.Environ()
495+
cmdDependency.Env = append(cmdDependency.Env, kubeCfg)
496+
cmdDependency.Stderr = os.Stderr
497+
cmdDependency.Stdout = os.Stdout
498+
if errCmd := cmdDependency.Run(); errCmd != nil {
499+
return fmt.Errorf("Cannot execute helm dependency update : %v", errCmd)
500+
}
501+
}
502+
503+
cmdGet := exec.Command(binaryName, "get", "all", "--namespace="+namespace, application)
504+
cmdGet.Env = os.Environ()
505+
cmdGet.Env = append(cmdGet.Env, kubeCfg)
506+
errCmd := cmdGet.Run()
507+
508+
var args []string
509+
if errCmd != nil { // Install
510+
fmt.Printf("Install helm release '%s' with chart '%s'...\n", application, helmChart)
511+
args = []string{"install", "--debug", "--timeout=" + timeoutStr, "--wait=true", "--namespace=" + namespace}
512+
if helmValues != "" {
513+
args = append(args, "-f", helmValues)
514+
}
515+
516+
helmChartArgs := strings.Split(helmChart, " ")
517+
if len(helmChartArgs) > 1 {
518+
args = append(args, "--repo="+helmChartArgs[0], helmChartArgs[1])
519+
args = append(args, application, helmChartArgs[1])
520+
} else {
521+
args = append(args, application, helmChart)
522+
}
523+
} else {
524+
fmt.Printf("Update helm release '%s' with chart '%s'...\n", application, helmChart)
525+
args = []string{"upgrade", "--timeout=" + timeoutStr, "--wait=true", "--namespace=" + namespace}
526+
if helmValues != "" {
527+
args = append(args, "-f", helmValues)
528+
}
529+
530+
helmChartArgs := strings.Split(helmChart, " ")
531+
if len(helmChartArgs) > 1 {
532+
args = append(args, "--repo="+helmChartArgs[0], application, helmChartArgs[1])
533+
} else {
534+
args = append(args, application, helmChart)
535+
}
536+
}
537+
538+
fmt.Printf("Execute: helm %s\n", strings.Join(args, " "))
539+
cmd := exec.Command(binaryName, args...)
540+
cmd.Env = os.Environ()
541+
cmd.Env = append(cmd.Env, kubeCfg)
542+
cmd.Stderr = os.Stderr
543+
cmd.Stdout = os.Stdout
544+
if err := cmd.Run(); err != nil {
545+
return fmt.Errorf("Cannot execute helm install/update : %v", err)
546+
}
547+
548+
return nil
549+
}
550+
405551
func writeHelmBinary(pathname string, gzipStream io.Reader) error {
406552
uncompressedStream, err := gzip.NewReader(gzipStream)
407553
if err != nil {

docs/content/docs/concepts/files/application-syntax.md

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ deployments:
4848
value: deploy/helm/
4949
helm_values:
5050
type: deploy/helm/values.yaml
51+
helm_version:
52+
type: 2.12.2
5153
```
5254
5355
## Variables
@@ -148,6 +150,8 @@ deployments:
148150
value: deploy/helm/
149151
helm_values:
150152
type: deploy/helm/values-cluster-A.yaml
153+
helm_version:
154+
type: 2.12.2
151155
152156
my-kubernetes-cluster-B:
153157
namespace:
@@ -156,6 +160,8 @@ deployments:
156160
value: deploy/helm/
157161
helm_values:
158162
type: deploy/helm/values-cluster-B.yaml
163+
helm_version:
164+
type: 2.12.2
159165
```
160166

161167
The list of the availabe deployment platform is available from the Web UI on the `project / integration` section, or with the command `cdsctl project integration list`

docs/content/docs/integrations/kubernetes/kubernetes_deployment.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ deployment_default_config:
2929
timeout:
3030
type: string
3131
value: 180
32-
description: timeout in seconds
32+
description: timeout in seconds for v2 or duration for v3 (ex: 3m)
3333
namespace:
3434
type: string
3535
value: default
@@ -43,6 +43,9 @@ deployment_default_config:
4343
helm_values:
4444
type: string
4545
description: specify helm values in a YAML file or a URL to configure/override your helm chart
46+
helm_version:
47+
type: string
48+
description: specify helm version to use (default: v2.12.2)
4649
```
4750
4851
Import the integration with :
@@ -126,6 +129,10 @@ model:
126129
type: string
127130
description: specify helm values in a YAML file or a URL to configure/override
128131
your helm chart
132+
helm_version:
133+
value: ""
134+
type: string
135+
description: specify helm version to use (default: v2.12.2)
129136
namespace:
130137
value: default
131138
type: string
@@ -185,6 +192,10 @@ deployment_default_config:
185192
value: ""
186193
type: string
187194
description: specify helm values in a YAML file or a URL to configure/override your helm chart
195+
helm_version:
196+
value: ""
197+
type: string
198+
description: specify helm version to use (default: v2.12.2)
188199
namespace:
189200
value: default
190201
type: string

0 commit comments

Comments
 (0)