Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(contrib): select helm version (#5058) #5066

Merged
merged 3 commits into from
Mar 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"]
yesnault marked this conversation as resolved.
Show resolved Hide resolved

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