Skip to content

Commit

Permalink
feat(contrib): select helm version (#5058) (#5066)
Browse files Browse the repository at this point in the history
Signed-off-by: Axel Dumortier <[email protected]>
  • Loading branch information
AxelDum authored Mar 18, 2020
1 parent 06bdf35 commit e1e4fc3
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 19 deletions.
7 changes: 5 additions & 2 deletions contrib/integrations/kubernetes/kubernetes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ deployment_default_config:
timeout:
type: string
value: 180
description: timeout in seconds
description: "timeout in seconds for v2 or duration for v3 (ex: 3m)"
namespace:
type: string
value: default
description: Kubernetes namespace in which you want to deploy your components (OPTIONAL)
description: "Kubernetes namespace in which you want to deploy your components (OPTIONAL)"
deployment_files:
type: string
description: Glob to yaml filepaths
Expand All @@ -29,3 +29,6 @@ deployment_default_config:
helm_values:
type: string
description: specify helm values in a YAML file or a URL to configure/override your helm chart
helm_version:
type: string
description: "specify helm version to use (default: 2.12.2)"
178 changes: 162 additions & 16 deletions contrib/integrations/kubernetes/plugin-kubernetes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"time"

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

func executeHelm(q *integrationplugin.DeployQuery) error {
releaseName := q.GetOptions()["cds.integration.release_name"]
namespace := q.GetOptions()["cds.integration.namespace"]
helmChart := q.GetOptions()["cds.integration.helm_chart"]
helmValues := q.GetOptions()["cds.integration.helm_values"]
timeoutStr := q.GetOptions()["cds.integration.timeout"]
project := q.GetOptions()["cds.project"]
workflow := q.GetOptions()["cds.workflow"]
application := q.GetOptions()["cds.application"]
if namespace == "" {
namespace = "default"
helmVersion := q.GetOptions()["cds.integration.helm_version"]

if helmVersion == "" {
helmVersion = "2.12.2"
}
if releaseName != "" {
application = releaseName

version, err := semver.Parse(helmVersion)
if err != nil {
return fmt.Errorf("Invalid Helm Version %s - err: %v", helmVersion, err)
}
supportedVersion := ">=2.0.0 <4.0.0"
expectedRange, err := semver.ParseRange(supportedVersion)
if err != nil {
return fmt.Errorf("Fail to parse semver range : %v", err)
}

helmFound := false
if _, err := exec.LookPath("helm"); err == nil {
helmFound = true
if !expectedRange(version) {
return fmt.Errorf("Unsupported helm version, should be : %s", supportedVersion)
}

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

binaryName := "helm"
kubeCfg := "KUBECONFIG=" + path.Join(cwd, ".kube/config")

helmFound := false
if _, err := exec.LookPath("helm"); err == nil {
helmFound = true
}

if helmFound {
out, err := exec.Command(binaryName, "version", "--client", "--short").Output()
if err != nil {
return fmt.Errorf("Cannot check helm version : %v", err)
}
installedHelm := strings.TrimPrefix(string(out), "Client: ")
installedVersion, err := semver.ParseTolerant(installedHelm)
if err != nil {
return fmt.Errorf("Invalid installed Helm Version %s - err: %v", installedHelm, err)
}

if !version.Equals(installedVersion) {
fmt.Println("Helm in path is not at correct version, need installation")
fmt.Printf("Path version : %s\n", installedVersion.String())
fmt.Printf("Target version : %s\n", version.String())
helmFound = false
}
}

if !helmFound {
fmt.Println("Download helm in progress...")
fmt.Printf("Download helm %s in progress...\n", version.String())
netClient := &http.Client{
Timeout: time.Second * 600,
}
response, err := netClient.Get("https://storage.googleapis.com/kubernetes-helm/helm-v2.12.2-" + sdk.GOOS + "-" + sdk.GOARCH + ".tar.gz")
response, err := netClient.Get(fmt.Sprintf("https://get.helm.sh/helm-v%s-%s-%s.tar.gz", version.String(), sdk.GOOS, sdk.GOARCH))
if err != nil {
return fmt.Errorf("Cannot download helm : %v", err)
}
Expand All @@ -334,14 +364,43 @@ func executeHelm(q *integrationplugin.DeployQuery) error {
binaryName = path.Join(".", binaryName, sdk.GOOS+"-"+sdk.GOARCH, "helm")
}

switch version.Major {
case 2:
return executeHelmV2(binaryName, kubeCfg, q)
case 3:
return executeHelmV3(binaryName, kubeCfg, q)
}

return fmt.Errorf("Unsupported helm version")
}

func executeHelmV2(binaryName, kubeCfg string, q *integrationplugin.DeployQuery) error {
releaseName := q.GetOptions()["cds.integration.release_name"]
namespace := q.GetOptions()["cds.integration.namespace"]
helmChart := q.GetOptions()["cds.integration.helm_chart"]
helmValues := q.GetOptions()["cds.integration.helm_values"]
timeoutStr := q.GetOptions()["cds.integration.timeout"]
application := q.GetOptions()["cds.application"]

if namespace == "" {
namespace = "default"
}
if releaseName != "" {
application = releaseName
}

if d, err := time.ParseDuration(timeoutStr); err == nil {
timeoutStr = strconv.Itoa(int(d.Seconds()))
fmt.Println("timeout is a duration, converting timeout in seconds to " + timeoutStr)
}

cmdInit := exec.Command(binaryName, "init", "--client-only")
cmdInit.Env = os.Environ()
cmdInit.Stderr = os.Stderr
cmdInit.Stdout = os.Stdout
if err := cmdInit.Run(); err != nil {
return fmt.Errorf("Cannot execute helm init : %v", err)
}
kubeCfg := "KUBECONFIG=" + path.Join(cwd, ".kube/config")

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

func executeHelmV3(binaryName, kubeCfg string, q *integrationplugin.DeployQuery) error {
releaseName := q.GetOptions()["cds.integration.release_name"]
namespace := q.GetOptions()["cds.integration.namespace"]
helmChart := q.GetOptions()["cds.integration.helm_chart"]
helmValues := q.GetOptions()["cds.integration.helm_values"]
timeoutStr := q.GetOptions()["cds.integration.timeout"]
application := q.GetOptions()["cds.application"]

if namespace == "" {
namespace = "default"
}
if releaseName != "" {
application = releaseName
}
if _, err := time.ParseDuration(timeoutStr); err != nil {
timeoutStr = timeoutStr + "s"
fmt.Println("timeout is not a duration, setting timeout to " + timeoutStr)
}

cmdRepoAdd := exec.Command(binaryName, "repo", "add", "stable", "https://kubernetes-charts.storage.googleapis.com/")
cmdRepoAdd.Env = os.Environ()
cmdRepoAdd.Stderr = os.Stderr
cmdRepoAdd.Stdout = os.Stdout
if err := cmdRepoAdd.Run(); err != nil {
return fmt.Errorf("Cannot execute helm repo add stable : %v", err)
}

if _, err := os.Stat(helmChart); err == nil {
fmt.Println("Helm dependency update")
cmdDependency := exec.Command(binaryName, "dependency", "update", helmChart)
cmdDependency.Env = os.Environ()
cmdDependency.Env = append(cmdDependency.Env, kubeCfg)
cmdDependency.Stderr = os.Stderr
cmdDependency.Stdout = os.Stdout
if errCmd := cmdDependency.Run(); errCmd != nil {
return fmt.Errorf("Cannot execute helm dependency update : %v", errCmd)
}
}

cmdGet := exec.Command(binaryName, "get", "all", "--namespace="+namespace, application)
cmdGet.Env = os.Environ()
cmdGet.Env = append(cmdGet.Env, kubeCfg)
errCmd := cmdGet.Run()

var args []string
if errCmd != nil { // Install
fmt.Printf("Install helm release '%s' with chart '%s'...\n", application, helmChart)
args = []string{"install", "--debug", "--timeout=" + timeoutStr, "--wait=true", "--namespace=" + namespace}
if helmValues != "" {
args = append(args, "-f", helmValues)
}

helmChartArgs := strings.Split(helmChart, " ")
if len(helmChartArgs) > 1 {
args = append(args, "--repo="+helmChartArgs[0], helmChartArgs[1])
args = append(args, application, helmChartArgs[1])
} else {
args = append(args, application, helmChart)
}
} else {
fmt.Printf("Update helm release '%s' with chart '%s'...\n", application, helmChart)
args = []string{"upgrade", "--timeout=" + timeoutStr, "--wait=true", "--namespace=" + namespace}
if helmValues != "" {
args = append(args, "-f", helmValues)
}

helmChartArgs := strings.Split(helmChart, " ")
if len(helmChartArgs) > 1 {
args = append(args, "--repo="+helmChartArgs[0], application, helmChartArgs[1])
} else {
args = append(args, application, helmChart)
}
}

fmt.Printf("Execute: helm %s\n", strings.Join(args, " "))
cmd := exec.Command(binaryName, args...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, kubeCfg)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
if err := cmd.Run(); err != nil {
return fmt.Errorf("Cannot execute helm install/update : %v", err)
}

return nil
}

func writeHelmBinary(pathname string, gzipStream io.Reader) error {
uncompressedStream, err := gzip.NewReader(gzipStream)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions docs/content/docs/concepts/files/application-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ deployments:
value: deploy/helm/
helm_values:
type: deploy/helm/values.yaml
helm_version:
type: 2.12.2
```
## Variables
Expand Down Expand Up @@ -148,6 +150,8 @@ deployments:
value: deploy/helm/
helm_values:
type: deploy/helm/values-cluster-A.yaml
helm_version:
type: 2.12.2
my-kubernetes-cluster-B:
namespace:
Expand All @@ -156,6 +160,8 @@ deployments:
value: deploy/helm/
helm_values:
type: deploy/helm/values-cluster-B.yaml
helm_version:
type: 2.12.2
```

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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ deployment_default_config:
timeout:
type: string
value: 180
description: timeout in seconds
description: timeout in seconds for v2 or duration for v3 (ex: 3m)
namespace:
type: string
value: default
Expand All @@ -43,6 +43,9 @@ deployment_default_config:
helm_values:
type: string
description: specify helm values in a YAML file or a URL to configure/override your helm chart
helm_version:
type: string
description: specify helm version to use (default: v2.12.2)
```
Import the integration with :
Expand Down Expand Up @@ -126,6 +129,10 @@ model:
type: string
description: specify helm values in a YAML file or a URL to configure/override
your helm chart
helm_version:
value: ""
type: string
description: specify helm version to use (default: v2.12.2)
namespace:
value: default
type: string
Expand Down Expand Up @@ -185,6 +192,10 @@ deployment_default_config:
value: ""
type: string
description: specify helm values in a YAML file or a URL to configure/override your helm chart
helm_version:
value: ""
type: string
description: specify helm version to use (default: v2.12.2)
namespace:
value: default
type: string
Expand Down

0 comments on commit e1e4fc3

Please sign in to comment.