mirror of
https://github.com/SAP/jenkins-library.git
synced 2026-06-19 22:58:55 +02:00
feat(k8s): Add basic support for Helm 3 in kubernetesDeploy step (#1438)
* Extends kubernetesDeploy step to support Helm 3 Currently, the kubernetesDeploy step has no support to Helm 3 due to the fact that: - the initialization command used works only for Helm 2 - the image used when running the helm CLI is based on Helm 2 The need for Helm 3 support comes from the fact that Helm 3 introduces major architectural changes, more specifically, the removal of its server-side agent called Tiller - thus, being incompatible with one another. This commit adds this support by introducing a new configuration field (helmVersion). By default, its values is set to 2 (Helm 2) to avoid breaking any existing functionalities. * Use deployTool field to decide between Helm 2 or 3 * Remove helm init and replace wait for atomic in v3 * Update cmd/kubernetesDeploy.go Nice catch! Co-Authored-By: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> * Add documentation for kubernetesDeploy step * Add helm3 example for kubernetesDeploy step using mandatory fields * Add new line at the end of kubernetesDeploy documentation * Link kubernetesDeploy step with docs generator * Add possible values for deployTool in kubernetesDeploy * dummy change * Revert "dummy change" Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
This commit is contained in:
co-authored by
Christopher Fenner
Oliver Nocon
parent
b942dcd954
commit
b335387eac
+16
-10
@@ -23,7 +23,7 @@ func kubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetry.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runKubernetesDeploy(config kubernetesDeployOptions, command execRunner, stdout io.Writer) {
|
func runKubernetesDeploy(config kubernetesDeployOptions, command execRunner, stdout io.Writer) {
|
||||||
if config.DeployTool == "helm" {
|
if config.DeployTool == "helm" || config.DeployTool == "helm3" {
|
||||||
runHelmDeploy(config, command, stdout)
|
runHelmDeploy(config, command, stdout)
|
||||||
} else {
|
} else {
|
||||||
runKubectlDeploy(config, command)
|
runKubectlDeploy(config, command)
|
||||||
@@ -48,16 +48,18 @@ func runHelmDeploy(config kubernetesDeployOptions, command execRunner, stdout io
|
|||||||
log.Entry().WithFields(helmLogFields).Debug("Calling Helm")
|
log.Entry().WithFields(helmLogFields).Debug("Calling Helm")
|
||||||
|
|
||||||
helmEnv := []string{fmt.Sprintf("KUBECONFIG=%v", config.KubeConfig)}
|
helmEnv := []string{fmt.Sprintf("KUBECONFIG=%v", config.KubeConfig)}
|
||||||
if len(config.TillerNamespace) > 0 {
|
if config.DeployTool == "helm" && len(config.TillerNamespace) > 0 {
|
||||||
helmEnv = append(helmEnv, fmt.Sprintf("TILLER_NAMESPACE=%v", config.TillerNamespace))
|
helmEnv = append(helmEnv, fmt.Sprintf("TILLER_NAMESPACE=%v", config.TillerNamespace))
|
||||||
}
|
}
|
||||||
log.Entry().Debugf("Helm SetEnv: %v", helmEnv)
|
log.Entry().Debugf("Helm SetEnv: %v", helmEnv)
|
||||||
command.SetEnv(helmEnv)
|
command.SetEnv(helmEnv)
|
||||||
command.Stdout(stdout)
|
command.Stdout(stdout)
|
||||||
|
|
||||||
initParams := []string{"init", "--client-only"}
|
if config.DeployTool == "helm" {
|
||||||
if err := command.RunExecutable("helm", initParams...); err != nil {
|
initParams := []string{"init", "--client-only"}
|
||||||
log.Entry().WithError(err).Fatal("Helm init called failed")
|
if err := command.RunExecutable("helm", initParams...); err != nil {
|
||||||
|
log.Entry().WithError(err).Fatal("Helm init call failed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dockerRegistrySecret bytes.Buffer
|
var dockerRegistrySecret bytes.Buffer
|
||||||
@@ -103,15 +105,19 @@ func runHelmDeploy(config kubernetesDeployOptions, command execRunner, stdout io
|
|||||||
config.ChartPath,
|
config.ChartPath,
|
||||||
"--install",
|
"--install",
|
||||||
"--force",
|
"--force",
|
||||||
"--namespace",
|
"--namespace", config.Namespace,
|
||||||
config.Namespace,
|
|
||||||
"--wait",
|
|
||||||
"--timeout",
|
|
||||||
strconv.Itoa(config.HelmDeployWaitSeconds),
|
|
||||||
"--set",
|
"--set",
|
||||||
fmt.Sprintf("image.repository=%v/%v,image.tag=%v,secret.dockerconfigjson=%v%v", containerRegistry, containerImageName, containerImageTag, dockerRegistrySecretData.Data.DockerConfJSON, ingressHosts),
|
fmt.Sprintf("image.repository=%v/%v,image.tag=%v,secret.dockerconfigjson=%v%v", containerRegistry, containerImageName, containerImageTag, dockerRegistrySecretData.Data.DockerConfJSON, ingressHosts),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.DeployTool == "helm" {
|
||||||
|
upgradeParams = append(upgradeParams, "--wait", "--timeout", strconv.Itoa(config.HelmDeployWaitSeconds))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DeployTool == "helm3" {
|
||||||
|
upgradeParams = append(upgradeParams, "--atomic", "--timeout", fmt.Sprintf("%vs", config.HelmDeployWaitSeconds))
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.KubeContext) > 0 {
|
if len(config.KubeContext) > 0 {
|
||||||
upgradeParams = append(upgradeParams, "--kube-context", config.KubeContext)
|
upgradeParams = append(upgradeParams, "--kube-context", config.KubeContext)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/SAP/jenkins-library/pkg/mock"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SAP/jenkins-library/pkg/mock"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -57,11 +58,11 @@ func TestRunKubernetesDeploy(t *testing.T) {
|
|||||||
"--force",
|
"--force",
|
||||||
"--namespace",
|
"--namespace",
|
||||||
"deploymentNamespace",
|
"deploymentNamespace",
|
||||||
|
"--set",
|
||||||
|
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,ingress.hosts[0]=ingress.host1,ingress.hosts[1]=ingress.host2",
|
||||||
"--wait",
|
"--wait",
|
||||||
"--timeout",
|
"--timeout",
|
||||||
"400",
|
"400",
|
||||||
"--set",
|
|
||||||
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,ingress.hosts[0]=ingress.host1,ingress.hosts[1]=ingress.host2",
|
|
||||||
"--kube-context",
|
"--kube-context",
|
||||||
"testCluster",
|
"testCluster",
|
||||||
"--testParam",
|
"--testParam",
|
||||||
@@ -69,6 +70,58 @@ func TestRunKubernetesDeploy(t *testing.T) {
|
|||||||
}, e.Calls[2].Params, "Wrong upgrade parameters")
|
}, e.Calls[2].Params, "Wrong upgrade parameters")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("test helm v3", func(t *testing.T) {
|
||||||
|
opts := kubernetesDeployOptions{
|
||||||
|
ContainerRegistryURL: "https://my.registry:55555",
|
||||||
|
ContainerRegistryUser: "registryUser",
|
||||||
|
ContainerRegistryPassword: "********",
|
||||||
|
ChartPath: "path/to/chart",
|
||||||
|
DeploymentName: "deploymentName",
|
||||||
|
DeployTool: "helm3",
|
||||||
|
HelmDeployWaitSeconds: 400,
|
||||||
|
IngressHosts: []string{"ingress.host1", "ingress.host2"},
|
||||||
|
Image: "path/to/Image:latest",
|
||||||
|
AdditionalParameters: []string{"--testParam", "testValue"},
|
||||||
|
KubeContext: "testCluster",
|
||||||
|
Namespace: "deploymentNamespace",
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`
|
||||||
|
|
||||||
|
e := mock.ExecMockRunner{
|
||||||
|
StdoutReturn: map[string]string{
|
||||||
|
"kubectl --insecure-skip-tls-verify=true create secret docker-registry regsecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=******** --dry-run=true --output=json": dockerConfigJSON,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
|
||||||
|
runKubernetesDeploy(opts, &e, &stdout)
|
||||||
|
|
||||||
|
assert.Equal(t, "kubectl", e.Calls[0].Exec, "Wrong secret creation command")
|
||||||
|
assert.Equal(t, []string{"--insecure-skip-tls-verify=true", "create", "secret", "docker-registry", "regsecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********", "--dry-run=true", "--output=json"}, e.Calls[0].Params, "Wrong secret creation parameters")
|
||||||
|
|
||||||
|
assert.Equal(t, "helm", e.Calls[1].Exec, "Wrong upgrade command")
|
||||||
|
assert.Equal(t, []string{
|
||||||
|
"upgrade",
|
||||||
|
"deploymentName",
|
||||||
|
"path/to/chart",
|
||||||
|
"--install",
|
||||||
|
"--force",
|
||||||
|
"--namespace",
|
||||||
|
"deploymentNamespace",
|
||||||
|
"--set",
|
||||||
|
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,ingress.hosts[0]=ingress.host1,ingress.hosts[1]=ingress.host2",
|
||||||
|
"--atomic",
|
||||||
|
"--timeout",
|
||||||
|
"400s",
|
||||||
|
"--kube-context",
|
||||||
|
"testCluster",
|
||||||
|
"--testParam",
|
||||||
|
"testValue",
|
||||||
|
}, e.Calls[1].Params, "Wrong upgrade parameters")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("test kubectl - create secret/kubeconfig", func(t *testing.T) {
|
t.Run("test kubectl - create secret/kubeconfig", func(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(dir) // clean up
|
defer os.RemoveAll(dir) // clean up
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# ${docGenStepName}
|
||||||
|
|
||||||
|
## ${docGenDescription}
|
||||||
|
|
||||||
|
## ${docGenParameters}
|
||||||
|
|
||||||
|
## ${docGenConfiguration}
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
kubernetesDeploy script: this
|
||||||
|
```
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
// Deploy a helm chart called "myChart" using Helm 3
|
||||||
|
kubernetesDeploy script: this, deployTool: 'helm3', chartPath: 'myChart', deploymentName: 'myRelease', image: 'nginx', containerRegistryUrl: 'https://docker.io'
|
||||||
|
```
|
||||||
@@ -63,6 +63,7 @@ nav:
|
|||||||
- jenkinsMaterializeLog: steps/jenkinsMaterializeLog.md
|
- jenkinsMaterializeLog: steps/jenkinsMaterializeLog.md
|
||||||
- kanikoExecute: steps/kanikoExecute.md
|
- kanikoExecute: steps/kanikoExecute.md
|
||||||
- karmaExecuteTests: steps/karmaExecuteTests.md
|
- karmaExecuteTests: steps/karmaExecuteTests.md
|
||||||
|
- kubernetesDeploy: steps/kubernetesDeploy.md
|
||||||
- mailSendNotification: steps/mailSendNotification.md
|
- mailSendNotification: steps/mailSendNotification.md
|
||||||
- mavenExecute: steps/mavenExecute.md
|
- mavenExecute: steps/mavenExecute.md
|
||||||
- mavenExecuteStaticCodeChecks: steps/mavenExecuteStaticCodeChecks.md
|
- mavenExecuteStaticCodeChecks: steps/mavenExecuteStaticCodeChecks.md
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ spec:
|
|||||||
- STAGES
|
- STAGES
|
||||||
- STEPS
|
- STEPS
|
||||||
default: kubectl
|
default: kubectl
|
||||||
|
possibleValues:
|
||||||
|
- kubectl
|
||||||
|
- helm
|
||||||
|
- helm3
|
||||||
- name: helmDeployWaitSeconds
|
- name: helmDeployWaitSeconds
|
||||||
type: int
|
type: int
|
||||||
description: Number of seconds before helm deploy returns.
|
description: Number of seconds before helm deploy returns.
|
||||||
@@ -211,6 +215,13 @@ spec:
|
|||||||
- STAGES
|
- STAGES
|
||||||
- STEPS
|
- STEPS
|
||||||
containers:
|
containers:
|
||||||
|
- image: dtzar/helm-kubectl:3.1.2
|
||||||
|
workingDir: /config
|
||||||
|
conditions:
|
||||||
|
- conditionRef: strings-equal
|
||||||
|
params:
|
||||||
|
- name: deployTool
|
||||||
|
value: helm3
|
||||||
- image: dtzar/helm-kubectl:2.12.1
|
- image: dtzar/helm-kubectl:2.12.1
|
||||||
workingDir: /config
|
workingDir: /config
|
||||||
conditions:
|
conditions:
|
||||||
|
|||||||
Reference in New Issue
Block a user