1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-09-16 09:26:22 +02:00

Add testing for helm during acceptance stage (#3402)

* Add kubernetesDeploy to Acceptance

* Add more kubernetesDeploy

* Add helm tests

* Change documentation

* Fix docu

* Change generated

* Add tests

* Add groovy tests

* Fix tests

* Change tests

Co-authored-by: Thorsten Duda <thorsten.duda@sap.com>
This commit is contained in:
Linda Siebert
2022-01-25 10:42:42 +01:00
committed by GitHub
parent 1a96d7a67e
commit 480d1c260f
6 changed files with 319 additions and 1 deletions

View File

@@ -225,6 +225,26 @@ func runHelmDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils,
if err := utils.RunExecutable("helm", upgradeParams...); err != nil {
log.Entry().WithError(err).Fatal("Helm upgrade call failed")
}
testParams := []string{
"test",
config.DeploymentName,
"--namespace", config.Namespace,
}
if config.ShowTestLogs {
testParams = append(
testParams,
"--logs",
)
}
if config.RunHelmTests {
if err := utils.RunExecutable("helm", testParams...); err != nil {
log.Entry().WithError(err).Fatal("Helm test call failed")
}
}
return nil
}

View File

@@ -35,6 +35,8 @@ type kubernetesDeployOptions struct {
Image string `json:"image,omitempty"`
IngressHosts []string `json:"ingressHosts,omitempty"`
KeepFailedDeployments bool `json:"keepFailedDeployments,omitempty"`
RunHelmTests bool `json:"runHelmTests,omitempty"`
ShowTestLogs bool `json:"showTestLogs,omitempty"`
KubeConfig string `json:"kubeConfig,omitempty"`
KubeContext string `json:"kubeContext,omitempty"`
KubeToken string `json:"kubeToken,omitempty"`
@@ -174,6 +176,8 @@ func addKubernetesDeployFlags(cmd *cobra.Command, stepConfig *kubernetesDeployOp
cmd.Flags().StringVar(&stepConfig.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.")
cmd.Flags().StringSliceVar(&stepConfig.IngressHosts, "ingressHosts", []string{}, "(Deprecated) List of ingress hosts to be exposed via helm deployment.")
cmd.Flags().BoolVar(&stepConfig.KeepFailedDeployments, "keepFailedDeployments", false, "Defines whether a failed deployment will be purged")
cmd.Flags().BoolVar(&stepConfig.RunHelmTests, "runHelmTests", false, "Defines whether or not to run helm tests against the recently deployed release")
cmd.Flags().BoolVar(&stepConfig.ShowTestLogs, "showTestLogs", false, "Defines whether to print the pod logs after running helm tests")
cmd.Flags().StringVar(&stepConfig.KubeConfig, "kubeConfig", os.Getenv("PIPER_kubeConfig"), "Defines the path to the \"kubeconfig\" file.")
cmd.Flags().StringVar(&stepConfig.KubeContext, "kubeContext", os.Getenv("PIPER_kubeContext"), "Defines the context to use from the \"kubeconfig\" file.")
cmd.Flags().StringVar(&stepConfig.KubeToken, "kubeToken", os.Getenv("PIPER_kubeToken"), "Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.")
@@ -415,6 +419,24 @@ func kubernetesDeployMetadata() config.StepData {
Aliases: []config.Alias{},
Default: false,
},
{
Name: "runHelmTests",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
Default: false,
},
{
Name: "showTestLogs",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
Default: false,
},
{
Name: "kubeConfig",
ResourceRef: []config.ResourceReference{

View File

@@ -370,6 +370,237 @@ func TestRunKubernetesDeploy(t *testing.T) {
}, mockUtils.Calls[1].Params, "Wrong upgrade parameters")
})
t.Run("test helm v3 - runs helm tests", func(t *testing.T) {
opts := kubernetesDeployOptions{
ContainerRegistryURL: "https://my.registry:55555",
ContainerRegistryUser: "registryUser",
ContainerRegistryPassword: "dummy",
ContainerRegistrySecret: "testSecret",
ChartPath: "path/to/chart",
DeploymentName: "deploymentName",
DeployTool: "helm3",
ForceUpdates: true,
HelmDeployWaitSeconds: 400,
HelmValues: []string{"values1.yaml", "values2.yaml"},
Image: "path/to/Image:latest",
AdditionalParameters: []string{"--testParam", "testValue"},
KubeContext: "testCluster",
Namespace: "deploymentNamespace",
DockerConfigJSON: ".pipeline/docker/config.json",
RunHelmTests: true,
}
dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`
mockUtils := newKubernetesDeployMockUtils()
mockUtils.StdoutReturn = map[string]string{
`kubectl create secret generic testSecret --from-file=.dockerconfigjson=.pipeline/docker/config.json --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json`: dockerConfigJSON,
}
var stdout bytes.Buffer
runKubernetesDeploy(opts, mockUtils, &stdout)
assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command")
assert.Equal(t, []string{
"create",
"secret",
"generic",
"testSecret",
"--from-file=.dockerconfigjson=.pipeline/docker/config.json",
"--type=kubernetes.io/dockerconfigjson",
"--insecure-skip-tls-verify=true",
"--dry-run=client",
"--output=json"},
mockUtils.Calls[0].Params, "Wrong secret creation parameters")
assert.Equal(t, "helm", mockUtils.Calls[1].Exec, "Wrong upgrade command")
assert.Equal(t, []string{
"upgrade",
"deploymentName",
"path/to/chart",
"--values",
"values1.yaml",
"--values",
"values2.yaml",
"--install",
"--namespace",
"deploymentNamespace",
"--set",
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.name=testSecret,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,imagePullSecrets[0].name=testSecret",
"--force",
"--wait",
"--timeout",
"400s",
"--atomic",
"--kube-context",
"testCluster",
"--testParam",
"testValue",
}, mockUtils.Calls[1].Params, "Wrong upgrade parameters")
assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong test command")
assert.Equal(t, []string{
"test",
"deploymentName",
"--namespace",
"deploymentNamespace",
}, mockUtils.Calls[2].Params, "Wrong test parameters")
})
t.Run("test helm v3 - runs helm tests with logs", func(t *testing.T) {
opts := kubernetesDeployOptions{
ContainerRegistryURL: "https://my.registry:55555",
ContainerRegistryUser: "registryUser",
ContainerRegistryPassword: "dummy",
ContainerRegistrySecret: "testSecret",
ChartPath: "path/to/chart",
DeploymentName: "deploymentName",
DeployTool: "helm3",
ForceUpdates: true,
HelmDeployWaitSeconds: 400,
HelmValues: []string{"values1.yaml", "values2.yaml"},
Image: "path/to/Image:latest",
AdditionalParameters: []string{"--testParam", "testValue"},
KubeContext: "testCluster",
Namespace: "deploymentNamespace",
DockerConfigJSON: ".pipeline/docker/config.json",
RunHelmTests: true,
ShowTestLogs: true,
}
dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`
mockUtils := newKubernetesDeployMockUtils()
mockUtils.StdoutReturn = map[string]string{
`kubectl create secret generic testSecret --from-file=.dockerconfigjson=.pipeline/docker/config.json --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json`: dockerConfigJSON,
}
var stdout bytes.Buffer
runKubernetesDeploy(opts, mockUtils, &stdout)
assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command")
assert.Equal(t, []string{
"create",
"secret",
"generic",
"testSecret",
"--from-file=.dockerconfigjson=.pipeline/docker/config.json",
"--type=kubernetes.io/dockerconfigjson",
"--insecure-skip-tls-verify=true",
"--dry-run=client",
"--output=json"},
mockUtils.Calls[0].Params, "Wrong secret creation parameters")
assert.Equal(t, "helm", mockUtils.Calls[1].Exec, "Wrong upgrade command")
assert.Equal(t, []string{
"upgrade",
"deploymentName",
"path/to/chart",
"--values",
"values1.yaml",
"--values",
"values2.yaml",
"--install",
"--namespace",
"deploymentNamespace",
"--set",
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.name=testSecret,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,imagePullSecrets[0].name=testSecret",
"--force",
"--wait",
"--timeout",
"400s",
"--atomic",
"--kube-context",
"testCluster",
"--testParam",
"testValue",
}, mockUtils.Calls[1].Params, "Wrong upgrade parameters")
assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong test command")
assert.Equal(t, []string{
"test",
"deploymentName",
"--namespace",
"deploymentNamespace",
"--logs",
}, mockUtils.Calls[2].Params, "Wrong test parameters")
})
t.Run("test helm v3 - should not run helm tests", func(t *testing.T) {
opts := kubernetesDeployOptions{
ContainerRegistryURL: "https://my.registry:55555",
ContainerRegistryUser: "registryUser",
ContainerRegistryPassword: "dummy",
ContainerRegistrySecret: "testSecret",
ChartPath: "path/to/chart",
DeploymentName: "deploymentName",
DeployTool: "helm3",
ForceUpdates: true,
HelmDeployWaitSeconds: 400,
HelmValues: []string{"values1.yaml", "values2.yaml"},
Image: "path/to/Image:latest",
AdditionalParameters: []string{"--testParam", "testValue"},
KubeContext: "testCluster",
Namespace: "deploymentNamespace",
DockerConfigJSON: ".pipeline/docker/config.json",
RunHelmTests: false,
ShowTestLogs: true,
}
dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`
mockUtils := newKubernetesDeployMockUtils()
mockUtils.StdoutReturn = map[string]string{
`kubectl create secret generic testSecret --from-file=.dockerconfigjson=.pipeline/docker/config.json --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json`: dockerConfigJSON,
}
var stdout bytes.Buffer
runKubernetesDeploy(opts, mockUtils, &stdout)
assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command")
assert.Equal(t, []string{
"create",
"secret",
"generic",
"testSecret",
"--from-file=.dockerconfigjson=.pipeline/docker/config.json",
"--type=kubernetes.io/dockerconfigjson",
"--insecure-skip-tls-verify=true",
"--dry-run=client",
"--output=json"},
mockUtils.Calls[0].Params, "Wrong secret creation parameters")
assert.Equal(t, "helm", mockUtils.Calls[1].Exec, "Wrong upgrade command")
assert.Equal(t, []string{
"upgrade",
"deploymentName",
"path/to/chart",
"--values",
"values1.yaml",
"--values",
"values2.yaml",
"--install",
"--namespace",
"deploymentNamespace",
"--set",
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.name=testSecret,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,imagePullSecrets[0].name=testSecret",
"--force",
"--wait",
"--timeout",
"400s",
"--atomic",
"--kube-context",
"testCluster",
"--testParam",
"testValue",
}, mockUtils.Calls[1].Params, "Wrong upgrade parameters")
assert.Equal(t, 2, len(mockUtils.Calls), "Too many helm calls")
})
t.Run("test helm v3 - with containerImageName and containerImageTag instead of image", func(t *testing.T) {
opts := kubernetesDeployOptions{
ContainerRegistryURL: "https://my.registry:55555",

View File

@@ -260,6 +260,24 @@ spec:
- PARAMETERS
- STAGES
- STEPS
- name: runHelmTests
type: bool
description: Defines whether or not to run helm tests against the recently deployed release
default: false
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: showTestLogs
type: bool
description: Defines whether to print the pod logs after running helm tests
default: false
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: kubeConfig
type: string
description: Defines the path to the "kubeconfig" file.

View File

@@ -56,6 +56,11 @@ class PiperPipelineStageAcceptanceTest extends BasePiperTest {
stepParameters.neoDeploy = m
})
helper.registerAllowedMethod('kubernetesDeploy', [Map.class], {m ->
stepsCalled.add('kubernetesDeploy')
stepParameters.kubernetesDeploy = m
})
helper.registerAllowedMethod('gaugeExecuteTests', [Map.class], {m ->
stepsCalled.add('gaugeExecuteTests')
stepParameters.gaugeExecuteTests = m
@@ -89,7 +94,7 @@ class PiperPipelineStageAcceptanceTest extends BasePiperTest {
script: nullScript,
juStabUtils: utils
)
assertThat(stepsCalled, not(anyOf(hasItem('cloudFoundryDeploy'), hasItem('neoDeploy'), hasItem('healthExecuteCheck'), hasItem('newmanExecute'), hasItem('uiVeri5ExecuteTests'), hasItem('gaugeExecuteTests'))))
assertThat(stepsCalled, not(anyOf(hasItem('cloudFoundryDeploy'), hasItem('neoDeploy'), hasItem('kubernetesDeploy'), hasItem('healthExecuteCheck'), hasItem('newmanExecute'), hasItem('uiVeri5ExecuteTests'), hasItem('gaugeExecuteTests'))))
}
@@ -132,6 +137,19 @@ class PiperPipelineStageAcceptanceTest extends BasePiperTest {
assertThat(stepsCalled, not(hasItem('testsPublishResults')))
}
@Test
void testReleaseStageKubernetes() {
jsr.step.piperPipelineStageRelease(
script: nullScript,
juStabUtils: utils,
kubernetesDeploy: true
)
assertThat(stepsCalled, hasItem('kubernetesDeploy'))
assertThat(stepsCalled, not(hasItem('testsPublishResults')))
}
@Test
void testAcceptanceStageGauge() {

View File

@@ -17,6 +17,8 @@ import static com.sap.piper.Prerequisites.checkScript
'cloudFoundryDeploy',
/** Performs behavior-driven tests using Gauge test framework against the deployed application/service. */
'gaugeExecuteTests',
/** For Kubernetes use-cases: Performs deployment to Kubernetes landscape. */
'kubernetesDeploy',
/**
* Performs health check in order to prove one aspect of operational readiness.
* In order to be able to respond to health checks from infrastructure components (like load balancers) it is important to provide one unprotected application endpoint which allows a judgement about the health of your application.
@@ -58,6 +60,7 @@ void call(Map parameters = [:]) {
.mixin(parameters, PARAMETER_KEYS)
.addIfEmpty('multicloudDeploy', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.multicloudDeploy)
.addIfEmpty('cloudFoundryDeploy', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.cloudFoundryDeploy)
.addIfEmpty('kubernetesDeploy', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.kubernetesDeploy)
.addIfEmpty('gaugeExecuteTests', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.gaugeExecuteTests)
.addIfEmpty('healthExecuteCheck', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.healthExecuteCheck)
.addIfEmpty('neoDeploy', script.commonPipelineEnvironment.configuration.runStep?.get(stageName)?.neoDeploy)
@@ -88,6 +91,12 @@ void call(Map parameters = [:]) {
neoDeploy script: script
}
}
if (config.kubernetesDeploy){
durationMeasure(script: script, measurementName: 'deploy_release_kubernetes_duration') {
kubernetesDeploy script: script
}
}
}
if (config.healthExecuteCheck) {