diff --git a/cmd/kubernetesDeploy.go b/cmd/kubernetesDeploy.go index cf333e208..ca4188567 100644 --- a/cmd/kubernetesDeploy.go +++ b/cmd/kubernetesDeploy.go @@ -5,13 +5,15 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" + "os" + "path/filepath" "regexp" "strconv" "strings" - piperDocker "github.com/SAP/jenkins-library/pkg/docker" - "github.com/SAP/jenkins-library/pkg/command" + "github.com/SAP/jenkins-library/pkg/docker" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/telemetry" @@ -72,7 +74,7 @@ func runKubernetesDeploy(config kubernetesDeployOptions, utils kubernetesDeployU if config.DeployTool == "helm" || config.DeployTool == "helm3" { return runHelmDeploy(config, utils, stdout) } else if config.DeployTool == "kubectl" { - return runKubectlDeploy(config, utils) + return runKubectlDeploy(config, utils, stdout) } return fmt.Errorf("Failed to execute deployments") } @@ -127,15 +129,18 @@ func runHelmDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, } var secretsData string - if len(config.DockerConfigJSON) == 0 && (len(config.ContainerRegistryUser) == 0 || len(config.ContainerRegistryPassword) == 0) { - log.Entry().Info("No/incomplete container registry credentials and no docker config.json file provided: skipping secret creation") + if len(config.ContainerRegistryUser) == 0 && len(config.ContainerRegistryPassword) == 0 { + log.Entry().Info("No/incomplete container registry credentials provided: skipping secret creation") if len(config.ContainerRegistrySecret) > 0 { secretsData = fmt.Sprintf(",imagePullSecrets[0].name=%v", config.ContainerRegistrySecret) } } else { var dockerRegistrySecret bytes.Buffer utils.Stdout(&dockerRegistrySecret) - kubeSecretParams := defineKubeSecretParams(config, containerRegistry, utils) + err, kubeSecretParams := defineKubeSecretParams(config, containerRegistry, utils) + if err != nil { + log.Entry().WithError(err).Fatal("parameter definition for creating registry secret failed") + } log.Entry().Infof("Calling kubectl create secret --dry-run=true ...") log.Entry().Debugf("kubectl parameters %v", kubeSecretParams) if err := utils.RunExecutable("kubectl", kubeSecretParams...); err != nil { @@ -223,7 +228,7 @@ func runHelmDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, return nil } -func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils) error { +func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, stdout io.Writer) error { _, containerRegistry, err := splitRegistryURL(config.ContainerRegistryURL) if err != nil { log.Entry().WithError(err).Fatalf("Container registry url '%v' incorrect", config.ContainerRegistryURL) @@ -248,25 +253,41 @@ func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtil kubeParams = append(kubeParams, fmt.Sprintf("--token=%v", config.KubeToken)) } - if config.CreateDockerRegistrySecret { - if len(config.DockerConfigJSON) == 0 && (len(config.ContainerRegistryUser) == 0 || len(config.ContainerRegistryPassword) == 0) { - log.Entry().Fatal("Cannot create Container registry secret without proper registry username/password or docker config.json file") + utils.Stdout(stdout) + + if len(config.ContainerRegistryUser) == 0 && len(config.ContainerRegistryPassword) == 0 { + log.Entry().Info("No/incomplete container registry credentials provided: skipping secret creation") + } else { + err, kubeSecretParams := defineKubeSecretParams(config, containerRegistry, utils) + if err != nil { + log.Entry().WithError(err).Fatal("parameter definition for creating registry secret failed") + } + var dockerRegistrySecret bytes.Buffer + utils.Stdout(&dockerRegistrySecret) + log.Entry().Infof("Creating container registry secret '%v'", config.ContainerRegistrySecret) + kubeSecretParams = append(kubeSecretParams, kubeParams...) + log.Entry().Debugf("Running kubectl with following parameters: %v", kubeSecretParams) + if err := utils.RunExecutable("kubectl", kubeSecretParams...); err != nil { + log.Entry().WithError(err).Fatal("Creating container registry secret failed") } - // first check if secret already exists - kubeCheckParams := append(kubeParams, "get", "secret", config.ContainerRegistrySecret) + var dockerRegistrySecretData map[string]interface{} - // ToDo: always update the secret using a yaml definition - if err := utils.RunExecutable("kubectl", kubeCheckParams...); err != nil { - log.Entry().Infof("Registry secret '%v' does not exist, let's create it ...", config.ContainerRegistrySecret) - kubeSecretParams := defineKubeSecretParams(config, containerRegistry, utils) - kubeSecretParams = append(kubeParams, kubeSecretParams...) - log.Entry().Infof("Creating container registry secret '%v'", config.ContainerRegistrySecret) - log.Entry().Debugf("Running kubectl with following parameters: %v", kubeSecretParams) - if err := utils.RunExecutable("kubectl", kubeSecretParams...); err != nil { - log.Entry().WithError(err).Fatal("Creating container registry secret failed") - } + if err := json.Unmarshal(dockerRegistrySecret.Bytes(), &dockerRegistrySecretData); err != nil { + log.Entry().WithError(err).Fatal("Reading docker registry secret json failed") } + + // write the json output to a file + tmpFolder := getTempDirForKubeCtlJson() + defer os.RemoveAll(tmpFolder) // clean up + jsonData, _ := json.Marshal(dockerRegistrySecretData) + ioutil.WriteFile(filepath.Join(tmpFolder, "secret.json"), jsonData, 0777) + + kubeSecretApplyParams := []string{"apply", "-f", filepath.Join(tmpFolder, "secret.json")} + if err := utils.RunExecutable("kubectl", kubeSecretApplyParams...); err != nil { + log.Entry().WithError(err).Fatal("Creating container registry secret failed") + } + } appTemplate, err := utils.FileRead(config.AppTemplate) @@ -309,6 +330,14 @@ func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtil return nil } +func getTempDirForKubeCtlJson() string { + tmpFolder, err := ioutil.TempDir(".", "temp-") + if err != nil { + log.Entry().WithError(err).WithField("path", tmpFolder).Debug("creating temp directory failed") + } + return tmpFolder +} + func splitRegistryURL(registryURL string) (protocol, registry string, err error) { parts := strings.Split(registryURL, "://") if len(parts) != 2 || len(parts[1]) == 0 { @@ -333,44 +362,31 @@ func splitFullImageName(image string) (imageName, tag string, err error) { return "", "", fmt.Errorf("Failed to split image name '%v'", image) } -func defineKubeSecretParams(config kubernetesDeployOptions, containerRegistry string, utils kubernetesDeployUtils) []string { - kubeSecretParams := []string{ - "create", - "secret", - } - if config.DeployTool == "helm" || config.DeployTool == "helm3" { - kubeSecretParams = append( - kubeSecretParams, - "--insecure-skip-tls-verify=true", - "--dry-run=true", - "--output=json", - ) - } - +func defineKubeSecretParams(config kubernetesDeployOptions, containerRegistry string, utils kubernetesDeployUtils) (error, []string) { + targetPath := "" if len(config.DockerConfigJSON) > 0 { // first enhance config.json with additional pipeline-related credentials if they have been provided if len(containerRegistry) > 0 && len(config.ContainerRegistryUser) > 0 && len(config.ContainerRegistryPassword) > 0 { var err error - _, err = piperDocker.CreateDockerConfigJSON(containerRegistry, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", config.DockerConfigJSON, utils) + targetPath, err = docker.CreateDockerConfigJSON(containerRegistry, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", config.DockerConfigJSON, utils) if err != nil { log.Entry().Warningf("failed to update Docker config.json: %v", err) + return err, []string{} } } - return append( - kubeSecretParams, - "generic", - config.ContainerRegistrySecret, - fmt.Sprintf("--from-file=.dockerconfigjson=%v", config.DockerConfigJSON), - "--type=kubernetes.io/dockerconfigjson", - ) + } else { + return fmt.Errorf("no docker config json file found to update credentials '%v'", config.DockerConfigJSON), []string{} } - return append( - kubeSecretParams, - "docker-registry", + return nil, []string{ + "create", + "secret", + "generic", config.ContainerRegistrySecret, - fmt.Sprintf("--docker-server=%v", containerRegistry), - fmt.Sprintf("--docker-username=%v", config.ContainerRegistryUser), - fmt.Sprintf("--docker-password=%v", config.ContainerRegistryPassword), - ) + fmt.Sprintf("--from-file=.dockerconfigjson=%v", targetPath), + "--type=kubernetes.io/dockerconfigjson", + "--insecure-skip-tls-verify=true", + "--dry-run=client", + "--output=json", + } } diff --git a/cmd/kubernetesDeploy_generated.go b/cmd/kubernetesDeploy_generated.go index abd218473..f28a0f851 100644 --- a/cmd/kubernetesDeploy_generated.go +++ b/cmd/kubernetesDeploy_generated.go @@ -75,7 +75,7 @@ helm upgrade --install --force --namespace --docker-username= --docker-password= --dry-run=true --output=json'` + "`" + ``, +* ` + "`" + `dockerSecret` + "`" + ` will be calculated with a call to ` + "`" + `kubectl create secret generic --from-file=.dockerconfigjson= --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json` + "`" + ``, PreRunE: func(cmd *cobra.Command, _ []string) error { startTime = time.Now() log.SetStepName(STEP_NAME) @@ -179,7 +179,7 @@ func addKubernetesDeployFlags(cmd *cobra.Command, stepConfig *kubernetesDeployOp cmd.Flags().StringVar(&stepConfig.KubeToken, "kubeToken", os.Getenv("PIPER_kubeToken"), "Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.") 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.DockerConfigJSON, "dockerConfigJSON", `.pipeline/docker/config.json`, "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") @@ -494,7 +494,7 @@ func kubernetesDeployMetadata() config.StepData { Type: "string", Mandatory: false, Aliases: []config.Alias{}, - Default: os.Getenv("PIPER_dockerConfigJSON"), + Default: `.pipeline/docker/config.json`, }, { Name: "deployCommand", diff --git a/cmd/kubernetesDeploy_test.go b/cmd/kubernetesDeploy_test.go index 6c0ab923a..d9a3edd71 100644 --- a/cmd/kubernetesDeploy_test.go +++ b/cmd/kubernetesDeploy_test.go @@ -33,7 +33,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -45,13 +45,14 @@ func TestRunKubernetesDeploy(t *testing.T) { AdditionalParameters: []string{"--testParam", "testValue"}, KubeContext: "testCluster", Namespace: "deploymentNamespace", + DockerConfigJSON: ".pipeline/docker/config.json", } dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: dockerConfigJSON, + `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 @@ -62,7 +63,9 @@ func TestRunKubernetesDeploy(t *testing.T) { assert.Equal(t, []string{"init", "--client-only"}, mockUtils.Calls[0].Params, "Wrong init parameters") assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[1].Params, "Wrong secret creation parameters") + 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[1].Params, "Wrong secret creation parameters") assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong upgrade command") assert.Equal(t, []string{ @@ -90,7 +93,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -103,13 +106,14 @@ func TestRunKubernetesDeploy(t *testing.T) { AdditionalParameters: []string{"--testParam", "testValue"}, KubeContext: "testCluster", Namespace: "deploymentNamespace", + DockerConfigJSON: ".pipeline/docker/config.json", } dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: dockerConfigJSON, + `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 @@ -120,7 +124,17 @@ func TestRunKubernetesDeploy(t *testing.T) { assert.Equal(t, []string{"init", "--client-only"}, mockUtils.Calls[0].Params, "Wrong init parameters") assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[1].Params, "Wrong secret creation parameters") + 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[1].Params, "Wrong secret creation parameters") assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong upgrade command") @@ -129,27 +143,29 @@ func TestRunKubernetesDeploy(t *testing.T) { t.Run("test helm - docker config.json path passed as parameter", func(t *testing.T) { opts := kubernetesDeployOptions{ - ContainerRegistryURL: "https://my.registry:55555", - DockerConfigJSON: "/path/to/.docker/config.json", - ContainerRegistrySecret: "testSecret", - ChartPath: "path/to/chart", - DeploymentName: "deploymentName", - DeployTool: "helm", - ForceUpdates: true, - HelmDeployWaitSeconds: 400, - IngressHosts: []string{"ingress.host1", "ingress.host2"}, - Image: "path/to/Image:latest", - AdditionalParameters: []string{"--testParam", "testValue"}, - KubeContext: "testCluster", - Namespace: "deploymentNamespace", + ContainerRegistryURL: "https://my.registry:55555", + DockerConfigJSON: "/path/to/.docker/config.json", + ContainerRegistryUser: "registryUser", + ContainerRegistryPassword: "dummy", + ContainerRegistrySecret: "testSecret", + ChartPath: "path/to/chart", + DeploymentName: "deploymentName", + DeployTool: "helm", + ForceUpdates: true, + HelmDeployWaitSeconds: 400, + IngressHosts: []string{"ingress.host1", "ingress.host2"}, + Image: "path/to/Image:latest", + AdditionalParameters: []string{"--testParam", "testValue"}, + KubeContext: "testCluster", + Namespace: "deploymentNamespace", } k8sSecretSpec := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() - mockUtils.AddFile("/path/to/.docker/config.json", []byte("ThisIsOurBase64EncodedSecret==")) + mockUtils.AddFile("/path/to/.docker/config.json", []byte(`{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`)) mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json generic testSecret --from-file=.dockerconfigjson=/path/to/.docker/config.json --type=kubernetes.io/dockerconfigjson`: k8sSecretSpec, + `kubectl create secret generic testSecret --from-file=.dockerconfigjson=/path/to/.docker/config.json --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json`: k8sSecretSpec, } var stdout bytes.Buffer @@ -164,14 +180,14 @@ func TestRunKubernetesDeploy(t *testing.T) { assert.Equal(t, []string{ "create", "secret", - "--insecure-skip-tls-verify=true", - "--dry-run=true", - "--output=json", "generic", "testSecret", "--from-file=.dockerconfigjson=/path/to/.docker/config.json", - `--type=kubernetes.io/dockerconfigjson`, - }, mockUtils.Calls[1].Params, "Wrong secret creation parameters") + "--type=kubernetes.io/dockerconfigjson", + "--insecure-skip-tls-verify=true", + "--dry-run=client", + "--output=json"}, + mockUtils.Calls[1].Params, "Wrong secret creation parameters") assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong upgrade command") assert.Equal(t, []string{ @@ -199,7 +215,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -212,13 +228,14 @@ func TestRunKubernetesDeploy(t *testing.T) { KubeContext: "testCluster", Namespace: "deploymentNamespace", KeepFailedDeployments: true, + DockerConfigJSON: ".pipeline/docker/config.json", } k8sSecretSpec := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: k8sSecretSpec, + `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`: k8sSecretSpec, } var stdout bytes.Buffer @@ -229,7 +246,17 @@ func TestRunKubernetesDeploy(t *testing.T) { assert.Equal(t, []string{"init", "--client-only"}, mockUtils.Calls[0].Params, "Wrong init parameters") assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[1].Params, "Wrong secret creation parameters") + 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[1].Params, "Wrong secret creation parameters") assert.Equal(t, "helm", mockUtils.Calls[2].Exec, "Wrong upgrade command") assert.Equal(t, []string{ @@ -278,7 +305,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -290,13 +317,14 @@ func TestRunKubernetesDeploy(t *testing.T) { AdditionalParameters: []string{"--testParam", "testValue"}, KubeContext: "testCluster", Namespace: "deploymentNamespace", + DockerConfigJSON: ".pipeline/docker/config.json", } dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: dockerConfigJSON, + `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 @@ -304,7 +332,17 @@ func TestRunKubernetesDeploy(t *testing.T) { runKubernetesDeploy(opts, mockUtils, &stdout) assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[0].Params, "Wrong secret creation parameters") + 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{ @@ -336,7 +374,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -349,13 +387,14 @@ func TestRunKubernetesDeploy(t *testing.T) { AdditionalParameters: []string{"--testParam", "testValue"}, KubeContext: "testCluster", Namespace: "deploymentNamespace", + DockerConfigJSON: ".pipeline/docker/config.json", } dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: dockerConfigJSON, + `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 @@ -363,7 +402,17 @@ func TestRunKubernetesDeploy(t *testing.T) { runKubernetesDeploy(opts, mockUtils, &stdout) assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[0].Params, "Wrong secret creation parameters") + 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") @@ -397,7 +446,7 @@ func TestRunKubernetesDeploy(t *testing.T) { opts := kubernetesDeployOptions{ ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "testSecret", ChartPath: "path/to/chart", DeploymentName: "deploymentName", @@ -410,13 +459,14 @@ func TestRunKubernetesDeploy(t *testing.T) { KubeContext: "testCluster", Namespace: "deploymentNamespace", KeepFailedDeployments: true, + DockerConfigJSON: ".pipeline/docker/config.json", } dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.StdoutReturn = map[string]string{ - `kubectl create secret --insecure-skip-tls-verify=true --dry-run=true --output=json docker-registry testSecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=\*\*\*\*\*\*\*\*`: dockerConfigJSON, + `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 @@ -424,7 +474,17 @@ func TestRunKubernetesDeploy(t *testing.T) { runKubernetesDeploy(opts, mockUtils, &stdout) assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret creation command") - assert.Equal(t, []string{"create", "secret", "--insecure-skip-tls-verify=true", "--dry-run=true", "--output=json", "docker-registry", "testSecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********"}, mockUtils.Calls[0].Params, "Wrong secret creation parameters") + 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{ @@ -578,89 +638,13 @@ func TestRunKubernetesDeploy(t *testing.T) { }, mockUtils.Calls[0].Params, "Wrong upgrade parameters") }) - t.Run("test kubectl - create secret/kubeconfig", func(t *testing.T) { - - opts := kubernetesDeployOptions{ - AppTemplate: "path/to/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: "apply", - } - - kubeYaml := `kind: Deployment -metadata: -spec: - spec: - image: ` - - mockUtils := newKubernetesDeployMockUtils() - mockUtils.AddFile(opts.AppTemplate, []byte(kubeYaml)) - - mockUtils.ShouldFailOnCommand = map[string]error{ - "kubectl --insecure-skip-tls-verify=true --namespace=deploymentNamespace --context=testCluster get secret regSecret": fmt.Errorf("secret not found"), - } - var stdout bytes.Buffer - runKubernetesDeploy(opts, mockUtils, &stdout) - - assert.Equal(t, mockUtils.Env, []string{"KUBECONFIG=This is my kubeconfig"}) - - assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret lookup command") - assert.Equal(t, []string{ - "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - fmt.Sprintf("--context=%v", opts.KubeContext), - "get", - "secret", - opts.ContainerRegistrySecret, - }, mockUtils.Calls[0].Params, "kubectl parameters incorrect") - - assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong secret create command") - assert.Equal(t, []string{ - "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - fmt.Sprintf("--context=%v", opts.KubeContext), - "create", - "secret", - "docker-registry", - opts.ContainerRegistrySecret, - "--docker-server=my.registry:55555", - fmt.Sprintf("--docker-username=%v", opts.ContainerRegistryUser), - fmt.Sprintf("--docker-password=%v", opts.ContainerRegistryPassword), - }, mockUtils.Calls[1].Params, "kubectl parameters incorrect") - - assert.Equal(t, "kubectl", mockUtils.Calls[2].Exec, "Wrong apply command") - assert.Equal(t, []string{ - "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - fmt.Sprintf("--context=%v", opts.KubeContext), - "apply", - "--filename", - opts.AppTemplate, - "--testParam", - "testValue", - }, mockUtils.Calls[2].Params, "kubectl parameters incorrect") - - appTemplate, _ := mockUtils.FileRead(opts.AppTemplate) - assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest") - }) - t.Run("test kubectl - create secret from docker config.json", func(t *testing.T) { opts := kubernetesDeployOptions{ AppTemplate: "path/to/test.yaml", - DockerConfigJSON: "/path/to/.docker/config.json", ContainerRegistryURL: "https://my.registry:55555", ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", + ContainerRegistryPassword: "dummy", ContainerRegistrySecret: "regSecret", CreateDockerRegistrySecret: true, DeployTool: "kubectl", @@ -670,18 +654,22 @@ spec: KubeContext: "testCluster", Namespace: "deploymentNamespace", DeployCommand: "apply", + DockerConfigJSON: ".pipeline/docker/config.json", } kubeYaml := `kind: Deployment -metadata: -spec: - spec: - image: ` + metadata: + spec: + spec: + image: ` + + dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}` mockUtils := newKubernetesDeployMockUtils() mockUtils.AddFile(opts.AppTemplate, []byte(kubeYaml)) - mockUtils.ShouldFailOnCommand = map[string]error{ - "kubectl --insecure-skip-tls-verify=true --namespace=deploymentNamespace --context=testCluster get secret regSecret": fmt.Errorf("secret not found"), + + mockUtils.StdoutReturn = map[string]string{ + `kubectl create secret generic regSecret --from-file=.dockerconfigjson=.pipeline/docker/config.json --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json --insecure-skip-tls-verify=true --namespace=deploymentNamespace --context=testCluster`: dockerConfigJSON, } var stdout bytes.Buffer runKubernetesDeploy(opts, mockUtils, &stdout) @@ -689,73 +677,37 @@ spec: assert.Equal(t, mockUtils.Env, []string{"KUBECONFIG=This is my kubeconfig"}) assert.Equal(t, []string{ - "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - fmt.Sprintf("--context=%v", opts.KubeContext), "create", "secret", "generic", - opts.ContainerRegistrySecret, - fmt.Sprintf("--from-file=.dockerconfigjson=%v", opts.DockerConfigJSON), - `--type=kubernetes.io/dockerconfigjson`, - }, mockUtils.Calls[1].Params, "kubectl parameters incorrect") - }) - - t.Run("test kubectl - lookup secret/kubeconfig", func(t *testing.T) { - - opts := kubernetesDeployOptions{ - AppTemplate: "path/to/test.yaml", - ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", - ContainerRegistrySecret: "regSecret", - CreateDockerRegistrySecret: true, - DeployTool: "kubectl", - Image: "path/to/Image:latest", - KubeConfig: "This is my kubeconfig", - Namespace: "deploymentNamespace", - DeployCommand: "apply", - } - - mockUtils := newKubernetesDeployMockUtils() - mockUtils.AddFile(opts.AppTemplate, []byte("testYaml")) - - var stdout bytes.Buffer - runKubernetesDeploy(opts, mockUtils, &stdout) - - assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong secret lookup command") - assert.Equal(t, []string{ + "regSecret", + "--from-file=.dockerconfigjson=.pipeline/docker/config.json", + "--type=kubernetes.io/dockerconfigjson", "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - "get", - "secret", - opts.ContainerRegistrySecret, - }, mockUtils.Calls[0].Params, "kubectl parameters incorrect") - - assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong apply command") - assert.Equal(t, []string{ + "--dry-run=client", + "--output=json", "--insecure-skip-tls-verify=true", - fmt.Sprintf("--namespace=%v", opts.Namespace), - "apply", - "--filename", - opts.AppTemplate, - }, mockUtils.Calls[1].Params, "kubectl parameters incorrect") + "--namespace=deploymentNamespace", + "--context=testCluster", + }, + mockUtils.Calls[0].Params, "Wrong secret creation parameters") + + assert.Containsf(t, mockUtils.Calls[1].Params, "apply", "Wrong secret creation parameters") + assert.Containsf(t, mockUtils.Calls[1].Params, "-f", "Wrong secret creation parameters") }) t.Run("test kubectl - token only", func(t *testing.T) { opts := kubernetesDeployOptions{ - APIServer: "https://my.api.server", - AppTemplate: "path/to/test.yaml", - ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", - ContainerRegistrySecret: "regSecret", - DeployTool: "kubectl", - Image: "path/to/Image:latest", - KubeToken: "testToken", - Namespace: "deploymentNamespace", - DeployCommand: "apply", + APIServer: "https://my.api.server", + AppTemplate: "path/to/test.yaml", + ContainerRegistryURL: "https://my.registry:55555", + ContainerRegistrySecret: "regSecret", + DeployTool: "kubectl", + Image: "path/to/Image:latest", + KubeToken: "testToken", + Namespace: "deploymentNamespace", + DeployCommand: "apply", } mockUtils := newKubernetesDeployMockUtils() @@ -779,18 +731,16 @@ spec: t.Run("test kubectl - with containerImageName and containerImageTag instead of image", func(t *testing.T) { opts := kubernetesDeployOptions{ - APIServer: "https://my.api.server", - AppTemplate: "test.yaml", - ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", - ContainerRegistrySecret: "regSecret", - DeployTool: "kubectl", - ContainerImageTag: "latest", - ContainerImageName: "path/to/Image", - KubeConfig: "This is my kubeconfig", - Namespace: "deploymentNamespace", - DeployCommand: "apply", + APIServer: "https://my.api.server", + AppTemplate: "test.yaml", + ContainerRegistryURL: "https://my.registry:55555", + ContainerRegistrySecret: "regSecret", + DeployTool: "kubectl", + ContainerImageTag: "latest", + ContainerImageName: "path/to/Image", + KubeConfig: "This is my kubeconfig", + Namespace: "deploymentNamespace", + DeployCommand: "apply", } mockUtils := newKubernetesDeployMockUtils() @@ -808,16 +758,14 @@ spec: t.Run("test kubectl - fails without image information", func(t *testing.T) { opts := kubernetesDeployOptions{ - APIServer: "https://my.api.server", - AppTemplate: "test.yaml", - ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", - ContainerRegistrySecret: "regSecret", - DeployTool: "kubectl", - KubeConfig: "This is my kubeconfig", - Namespace: "deploymentNamespace", - DeployCommand: "apply", + APIServer: "https://my.api.server", + AppTemplate: "test.yaml", + ContainerRegistryURL: "https://my.registry:55555", + ContainerRegistrySecret: "regSecret", + DeployTool: "kubectl", + KubeConfig: "This is my kubeconfig", + Namespace: "deploymentNamespace", + DeployCommand: "apply", } mockUtils := newKubernetesDeployMockUtils() @@ -833,8 +781,6 @@ spec: opts := kubernetesDeployOptions{ AppTemplate: "test.yaml", ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", ContainerRegistrySecret: "regSecret", CreateDockerRegistrySecret: true, DeployTool: "kubectl", @@ -847,10 +793,10 @@ spec: } kubeYaml := `kind: Deployment -metadata: -spec: - spec: - image: ` + metadata: + spec: + spec: + image: ` mockUtils := newKubernetesDeployMockUtils() mockUtils.AddFile("test.yaml", []byte(kubeYaml)) @@ -861,7 +807,7 @@ spec: assert.Equal(t, mockUtils.Env, []string{"KUBECONFIG=This is my kubeconfig"}) - assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong replace command") + assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong replace command") assert.Equal(t, []string{ "--insecure-skip-tls-verify=true", fmt.Sprintf("--namespace=%v", opts.Namespace), @@ -871,7 +817,7 @@ spec: opts.AppTemplate, "--testParam", "testValue", - }, mockUtils.Calls[1].Params, "kubectl parameters incorrect") + }, mockUtils.Calls[0].Params, "kubectl parameters incorrect") appTemplate, err := mockUtils.FileRead(opts.AppTemplate) assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest") @@ -881,8 +827,6 @@ spec: opts := kubernetesDeployOptions{ AppTemplate: "test.yaml", ContainerRegistryURL: "https://my.registry:55555", - ContainerRegistryUser: "registryUser", - ContainerRegistryPassword: "********", ContainerRegistrySecret: "regSecret", CreateDockerRegistrySecret: true, DeployTool: "kubectl", @@ -896,10 +840,10 @@ spec: } kubeYaml := `kind: Deployment -metadata: -spec: - spec: - image: ` + metadata: + spec: + spec: + image: ` mockUtils := newKubernetesDeployMockUtils() mockUtils.AddFile("test.yaml", []byte(kubeYaml)) @@ -910,7 +854,7 @@ spec: assert.Equal(t, mockUtils.Env, []string{"KUBECONFIG=This is my kubeconfig"}) - assert.Equal(t, "kubectl", mockUtils.Calls[1].Exec, "Wrong replace command") + assert.Equal(t, "kubectl", mockUtils.Calls[0].Exec, "Wrong replace command") assert.Equal(t, []string{ "--insecure-skip-tls-verify=true", fmt.Sprintf("--namespace=%v", opts.Namespace), @@ -921,7 +865,7 @@ spec: "--force", "--testParam", "testValue", - }, mockUtils.Calls[1].Params, "kubectl parameters incorrect") + }, mockUtils.Calls[0].Params, "kubectl parameters incorrect") appTemplate, err := mockUtils.FileRead(opts.AppTemplate) assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest") diff --git a/resources/metadata/kubernetesDeploy.yaml b/resources/metadata/kubernetesDeploy.yaml index 536048ab9..29d62d99d 100644 --- a/resources/metadata/kubernetesDeploy.yaml +++ b/resources/metadata/kubernetesDeploy.yaml @@ -22,7 +22,7 @@ metadata: * `yourRegistry` will be retrieved from `containerRegistryUrl` * `yourImageName`, `yourImageTag` will be retrieved from `image` - * `dockerSecret` will be calculated with a call to `kubectl create secret docker-registry regsecret --docker-server= --docker-username= --docker-password= --dry-run=true --output=json'` + * `dockerSecret` will be calculated with a call to `kubectl create secret generic --from-file=.dockerconfigjson= --type=kubernetes.io/dockerconfigjson --insecure-skip-tls-verify=true --dry-run=client --output=json` spec: inputs: secrets: @@ -157,10 +157,10 @@ spec: **For `deployTool: helm/helm3`:**
If `containerRegistryUser` and `containerRegistryPassword` are provided, a secret is created on the fly and the information is passed to the helm template.
- If neither `containerRegistryUser` nor `containerRegistryPassword` are provided, it is expected that a secret with the configured name exists in the target Kubernetes cluster.
- **For `deployTool: kubectl`:**
- If `createDockerRegistrySecret: true` and `containerRegistryUser` and `containerRegistryPassword` are provided, a secret with the given name will be created in the Kubernetes cluster unless a secret with the name already exists. + If `containerRegistryUser` and `containerRegistryPassword` are provided, a secret with the given name will be created in the Kubernetes cluster. + + If neither `containerRegistryUser` nor `containerRegistryPassword` are provided, it is expected that a secret with the configured name exists in the target Kubernetes cluster.
type: string scope: - PARAMETERS @@ -322,6 +322,7 @@ spec: - STAGES - STEPS secret: true + default: '.pipeline/docker/config.json' resourceRef: - name: dockerConfigJsonCredentialsId type: secret