1
0
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:
Oliver Feldmann
2021-11-04 14:11:31 +01:00
committed by GitHub
parent 874a314bf7
commit 0c64917ad5
4 changed files with 152 additions and 8 deletions

View File

@@ -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

View File

@@ -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{

View File

@@ -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) {