You've already forked sap-jenkins-library
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:
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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{
|
||||
|
@@ -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",
|
||||
|
@@ -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.
|
||||
|
@@ -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() {
|
||||
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user