You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-11-06 09:09:19 +02:00
kubernetesDeploy - Add option to replace instead of apply (#3216)
* Add option to replace instead of apply * Use deploy command string And make --force optional * Better force description * Don't warn on apply + --force * Improve description Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com> Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com>
This commit is contained in:
@@ -268,13 +268,17 @@ func runKubectlDeploy(config kubernetesDeployOptions, command command.ExecRunner
|
||||
log.Entry().WithError(err).Fatalf("Error when updating appTemplate '%v'", config.AppTemplate)
|
||||
}
|
||||
|
||||
kubeApplyParams := append(kubeParams, "apply", "--filename", config.AppTemplate)
|
||||
if len(config.AdditionalParameters) > 0 {
|
||||
kubeApplyParams = append(kubeApplyParams, config.AdditionalParameters...)
|
||||
kubeParams = append(kubeParams, config.DeployCommand, "--filename", config.AppTemplate)
|
||||
if config.ForceUpdates == true && config.DeployCommand == "replace" {
|
||||
kubeParams = append(kubeParams, "--force")
|
||||
}
|
||||
|
||||
if err := command.RunExecutable("kubectl", kubeApplyParams...); err != nil {
|
||||
log.Entry().Debugf("Running kubectl with following parameters: %v", kubeApplyParams)
|
||||
if len(config.AdditionalParameters) > 0 {
|
||||
kubeParams = append(kubeParams, config.AdditionalParameters...)
|
||||
}
|
||||
|
||||
if err := command.RunExecutable("kubectl", kubeParams...); err != nil {
|
||||
log.Entry().Debugf("Running kubectl with following parameters: %v", kubeParams)
|
||||
log.Entry().WithError(err).Fatal("Deployment with kubectl failed.")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -41,6 +41,7 @@ type kubernetesDeployOptions struct {
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
TillerNamespace string `json:"tillerNamespace,omitempty"`
|
||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||
DeployCommand string `json:"deployCommand,omitempty" validate:"oneof=apply replace"`
|
||||
}
|
||||
|
||||
// KubernetesDeployCommand Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.
|
||||
@@ -162,7 +163,7 @@ func addKubernetesDeployFlags(cmd *cobra.Command, stepConfig *kubernetesDeployOp
|
||||
cmd.Flags().BoolVar(&stepConfig.CreateDockerRegistrySecret, "createDockerRegistrySecret", false, "Only for `deployTool:kubectl`: Toggle to turn on `containerRegistrySecret` creation.")
|
||||
cmd.Flags().StringVar(&stepConfig.DeploymentName, "deploymentName", os.Getenv("PIPER_deploymentName"), "Defines the name of the deployment. It is a mandatory parameter when `deployTool:helm` or `deployTool:helm3`.")
|
||||
cmd.Flags().StringVar(&stepConfig.DeployTool, "deployTool", `kubectl`, "Defines the tool which should be used for deployment.")
|
||||
cmd.Flags().BoolVar(&stepConfig.ForceUpdates, "forceUpdates", true, "Helm only: force resource updates with helm parameter `--force`")
|
||||
cmd.Flags().BoolVar(&stepConfig.ForceUpdates, "forceUpdates", true, "Adds `--force` flag to a helm resource update command or to a kubectl replace command")
|
||||
cmd.Flags().IntVar(&stepConfig.HelmDeployWaitSeconds, "helmDeployWaitSeconds", 300, "Number of seconds before helm deploy returns.")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.HelmValues, "helmValues", []string{}, "List of helm values as YAML file reference or URL (as per helm parameter description for `-f` / `--values`)")
|
||||
cmd.Flags().StringVar(&stepConfig.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.")
|
||||
@@ -174,6 +175,7 @@ func addKubernetesDeployFlags(cmd *cobra.Command, stepConfig *kubernetesDeployOp
|
||||
cmd.Flags().StringVar(&stepConfig.Namespace, "namespace", `default`, "Defines the target Kubernetes namespace for the deployment.")
|
||||
cmd.Flags().StringVar(&stepConfig.TillerNamespace, "tillerNamespace", os.Getenv("PIPER_tillerNamespace"), "Defines optional tiller namespace for deployments using helm.")
|
||||
cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).")
|
||||
cmd.Flags().StringVar(&stepConfig.DeployCommand, "deployCommand", `apply`, "Only for `deployTool: kubectl`: defines the command `apply` or `replace`. The default is `apply`.")
|
||||
|
||||
cmd.MarkFlagRequired("containerRegistryUrl")
|
||||
cmd.MarkFlagRequired("deployTool")
|
||||
@@ -345,7 +347,7 @@ func kubernetesDeployMetadata() config.StepData {
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "bool",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Aliases: []config.Alias{{Name: "force"}},
|
||||
Default: true,
|
||||
},
|
||||
{
|
||||
@@ -484,6 +486,15 @@ func kubernetesDeployMetadata() config.StepData {
|
||||
Aliases: []config.Alias{},
|
||||
Default: os.Getenv("PIPER_dockerConfigJSON"),
|
||||
},
|
||||
{
|
||||
Name: "deployCommand",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Default: `apply`,
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []config.Container{
|
||||
|
||||
@@ -587,6 +587,7 @@ func TestRunKubernetesDeploy(t *testing.T) {
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
kubeYaml := `kind: Deployment
|
||||
@@ -666,6 +667,7 @@ spec:
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
kubeYaml := `kind: Deployment
|
||||
@@ -715,6 +717,7 @@ spec:
|
||||
Image: "path/to/Image:latest",
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("testYaml"), 0755)
|
||||
@@ -759,6 +762,7 @@ spec:
|
||||
Image: "path/to/Image:latest",
|
||||
KubeToken: "testToken",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("testYaml"), 0755)
|
||||
@@ -798,6 +802,7 @@ spec:
|
||||
ContainerImageName: "path/to/Image",
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("image: <image-name>"), 0755)
|
||||
@@ -829,6 +834,7 @@ spec:
|
||||
DeployTool: "kubectl",
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "apply",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("testYaml"), 0755)
|
||||
@@ -839,6 +845,115 @@ spec:
|
||||
err = runKubernetesDeploy(opts, &e, &stdout)
|
||||
assert.EqualError(t, err, "image information not given - please either set image or containerImageName and containerImageTag")
|
||||
})
|
||||
|
||||
t.Run("test kubectl - use replace deploy command", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
assert.NoError(t, err, "Error when creating temp dir")
|
||||
|
||||
opts := kubernetesDeployOptions{
|
||||
AppTemplate: filepath.Join(dir, "test.yaml"),
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ContainerRegistrySecret: "regSecret",
|
||||
CreateDockerRegistrySecret: true,
|
||||
DeployTool: "kubectl",
|
||||
Image: "path/to/Image:latest",
|
||||
AdditionalParameters: []string{"--testParam", "testValue"},
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "replace",
|
||||
}
|
||||
|
||||
kubeYaml := `kind: Deployment
|
||||
metadata:
|
||||
spec:
|
||||
spec:
|
||||
image: <image-name>`
|
||||
|
||||
err = ioutil.WriteFile(opts.AppTemplate, []byte(kubeYaml), 0755)
|
||||
assert.NoError(t, err, "Error when writing app template file")
|
||||
|
||||
e := mock.ExecMockRunner{}
|
||||
var stdout bytes.Buffer
|
||||
err = runKubernetesDeploy(opts, &e, &stdout)
|
||||
assert.NoError(t, err, "Command should not fail")
|
||||
|
||||
assert.Equal(t, e.Env, []string{"KUBECONFIG=This is my kubeconfig"})
|
||||
|
||||
assert.Equal(t, "kubectl", e.Calls[1].Exec, "Wrong replace command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--context=%v", opts.KubeContext),
|
||||
"replace",
|
||||
"--filename",
|
||||
opts.AppTemplate,
|
||||
"--testParam",
|
||||
"testValue",
|
||||
}, e.Calls[1].Params, "kubectl parameters incorrect")
|
||||
|
||||
appTemplate, err := ioutil.ReadFile(opts.AppTemplate)
|
||||
assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest")
|
||||
})
|
||||
|
||||
t.Run("test kubectl - use replace --force deploy command", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
assert.NoError(t, err, "Error when creating temp dir")
|
||||
|
||||
opts := kubernetesDeployOptions{
|
||||
AppTemplate: filepath.Join(dir, "test.yaml"),
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ContainerRegistrySecret: "regSecret",
|
||||
CreateDockerRegistrySecret: true,
|
||||
DeployTool: "kubectl",
|
||||
Image: "path/to/Image:latest",
|
||||
AdditionalParameters: []string{"--testParam", "testValue"},
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
DeployCommand: "replace",
|
||||
ForceUpdates: true,
|
||||
}
|
||||
|
||||
kubeYaml := `kind: Deployment
|
||||
metadata:
|
||||
spec:
|
||||
spec:
|
||||
image: <image-name>`
|
||||
|
||||
err = ioutil.WriteFile(opts.AppTemplate, []byte(kubeYaml), 0755)
|
||||
assert.NoError(t, err, "Error when writing app template file")
|
||||
|
||||
e := mock.ExecMockRunner{}
|
||||
var stdout bytes.Buffer
|
||||
err = runKubernetesDeploy(opts, &e, &stdout)
|
||||
assert.NoError(t, err, "Command should not fail")
|
||||
|
||||
assert.Equal(t, e.Env, []string{"KUBECONFIG=This is my kubeconfig"})
|
||||
|
||||
assert.Equal(t, "kubectl", e.Calls[1].Exec, "Wrong replace command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--context=%v", opts.KubeContext),
|
||||
"replace",
|
||||
"--filename",
|
||||
opts.AppTemplate,
|
||||
"--force",
|
||||
"--testParam",
|
||||
"testValue",
|
||||
}, e.Calls[1].Params, "kubectl parameters incorrect")
|
||||
|
||||
appTemplate, err := ioutil.ReadFile(opts.AppTemplate)
|
||||
assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSplitRegistryURL(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user