From b566986fa7f7415cc3f3858d9fb6ccf999fac44d Mon Sep 17 00:00:00 2001 From: Cornelius Weig <22861411+corneliusweig@users.noreply.github.com> Date: Wed, 30 Jan 2019 23:44:56 +0100 Subject: [PATCH] Add new flag '--force' to run/deploy commands This flag controls, if resources may be re-created, if patching is not possible. Summary of changes: - Add force-deploy option to kubectl and kustomize deployer - Add force-deploy option to helm deployer - Reduce code duplication in helm_test.go Signed-off-by: Cornelius Weig <22861411+corneliusweig@users.noreply.github.com> --- cmd/skaffold/app/cmd/cmd.go | 1 + docs/content/en/docs/references/cli/_index.md | 4 + pkg/skaffold/config/options.go | 5 + pkg/skaffold/deploy/helm.go | 5 + pkg/skaffold/deploy/helm_test.go | 319 +++++------------- pkg/skaffold/deploy/kubectl.go | 1 + pkg/skaffold/deploy/kubectl/cli.go | 9 +- pkg/skaffold/deploy/kubectl_test.go | 27 +- pkg/skaffold/deploy/kustomize.go | 1 + pkg/skaffold/deploy/kustomize_test.go | 5 +- 10 files changed, 140 insertions(+), 237 deletions(-) diff --git a/cmd/skaffold/app/cmd/cmd.go b/cmd/skaffold/app/cmd/cmd.go index 5416bc546f1..bed2994ef64 100644 --- a/cmd/skaffold/app/cmd/cmd.go +++ b/cmd/skaffold/app/cmd/cmd.go @@ -142,6 +142,7 @@ func FlagToEnvVarName(f *pflag.Flag) string { func AddRunDeployFlags(cmd *cobra.Command) { cmd.Flags().BoolVar(&opts.Tail, "tail", false, "Stream logs from deployed objects") + cmd.Flags().BoolVar(&opts.Force, "force", false, "Recreate kubernetes resources if necessary for deployment (default: false, warning: might cause downtime!)") cmd.Flags().StringArrayVarP(&opts.CustomLabels, "label", "l", nil, "Add custom labels to deployed objects. Set multiple times for multiple labels.") } diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index 1ff0de3688f..98e2c218610 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -331,6 +331,7 @@ Flags: -d, --default-repo string Default repository value (overrides global config) --enable-rpc skaffold dev Enable gRPC for exposing Skaffold events (true by default for skaffold dev) -f, --filename string Filename or URL to the pipeline file (default "skaffold.yaml") + --force Recreate kubernetes resources if necessary for deployment (default: false, warning: might cause downtime!) --images strings A list of pre-built images to deploy --insecure-registry stringArray Target registries for built images which are not secure -l, --label stringArray Add custom labels to deployed objects. Set multiple times for multiple labels. @@ -356,6 +357,7 @@ Env vars: * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) +* `SKAFFOLD_FORCE` (same as `--force`) * `SKAFFOLD_IMAGES` (same as `--images`) * `SKAFFOLD_INSECURE_REGISTRY` (same as `--insecure-registry`) * `SKAFFOLD_LABEL` (same as `--label`) @@ -521,6 +523,7 @@ Flags: -d, --default-repo string Default repository value (overrides global config) --enable-rpc skaffold dev Enable gRPC for exposing Skaffold events (true by default for skaffold dev) -f, --filename string Filename or URL to the pipeline file (default "skaffold.yaml") + --force Recreate kubernetes resources if necessary for deployment (default: false, warning: might cause downtime!) --insecure-registry stringArray Target registries for built images which are not secure -l, --label stringArray Add custom labels to deployed objects. Set multiple times for multiple labels. -n, --namespace string Run deployments in the specified namespace @@ -546,6 +549,7 @@ Env vars: * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILENAME` (same as `--filename`) +* `SKAFFOLD_FORCE` (same as `--force`) * `SKAFFOLD_INSECURE_REGISTRY` (same as `--insecure-registry`) * `SKAFFOLD_LABEL` (same as `--label`) * `SKAFFOLD_NAMESPACE` (same as `--namespace`) diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go index f940e6c4ca6..e3e180d8830 100644 --- a/pkg/skaffold/config/options.go +++ b/pkg/skaffold/config/options.go @@ -40,6 +40,7 @@ type SkaffoldOptions struct { CacheArtifacts bool ExperimentalGUI bool EnableRPC bool + Force bool NoPrune bool CustomTag string Namespace string @@ -90,3 +91,7 @@ func (opts *SkaffoldOptions) Labels() map[string]string { func (opts *SkaffoldOptions) Prune() bool { return !opts.NoPrune && !opts.CacheArtifacts } + +func (opts *SkaffoldOptions) ForceDeploy() bool { + return opts.Command == "dev" || opts.Force +} diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go index 027fe3676ae..e246711fbc9 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm.go @@ -48,6 +48,7 @@ type HelmDeployer struct { kubeContext string namespace string defaultRepo string + forceDeploy bool } // NewHelmDeployer returns a new HelmDeployer for a DeployConfig filled @@ -58,6 +59,7 @@ func NewHelmDeployer(runCtx *runcontext.RunContext) *HelmDeployer { kubeContext: runCtx.KubeContext, namespace: runCtx.Opts.Namespace, defaultRepo: runCtx.DefaultRepo, + forceDeploy: runCtx.Opts.ForceDeploy(), } } @@ -190,6 +192,9 @@ func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r lates } else { args = append(args, "upgrade", releaseName) args = append(args, h.Flags.Upgrade...) + if h.forceDeploy { + args = append(args, "--force") + } if r.RecreatePods { args = append(args, "--recreate-pods") } diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go index 62690627f90..6bffad4a0cd 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm_test.go @@ -285,93 +285,33 @@ func TestHelmDeploy(t *testing.T) { { description: "deploy success", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, }, { description: "deploy success with recreatePods", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployRecreatePodsConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeployRecreatePodsConfig, false), + builds: testBuilds, }, { description: "deploy success with skipBuildDependencies", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeploySkipBuildDependenciesConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeploySkipBuildDependenciesConfig, false), + builds: testBuilds, }, { description: "deploy error unmatched parameter", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfigParameterUnmatched, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, - shouldErr: true, + runContext: makeRunContext(testDeployConfigParameterUnmatched, false), + builds: testBuilds, + shouldErr: true, }, { description: "deploy success remote chart with skipBuildDependencies", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeploySkipBuildDependencies, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeploySkipBuildDependencies, false), + builds: testBuilds, }, { description: "deploy error remote chart without skipBuildDependencies", @@ -379,21 +319,9 @@ func TestHelmDeploy(t *testing.T) { t: t, depResult: fmt.Errorf("unexpected error"), }, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployRemoteChart, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, - shouldErr: true, + runContext: makeRunContext(testDeployRemoteChart, false), + builds: testBuilds, + shouldErr: true, }, { description: "get failure should install not upgrade", @@ -401,9 +329,9 @@ func TestHelmDeploy(t *testing.T) { t: t, getResult: fmt.Errorf("not found"), installMatcher: func(cmd *exec.Cmd) bool { - expected := map[string]bool{fmt.Sprintf("image=%s", testBuilds[0].Tag): true} + expected := fmt.Sprintf("image=%s", testBuilds[0].Tag) for _, arg := range cmd.Args { - if expected[arg] { + if expected == arg { return true } } @@ -411,20 +339,8 @@ func TestHelmDeploy(t *testing.T) { }, upgradeResult: fmt.Errorf("should not have called upgrade"), }, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, }, { description: "get failure should install not upgrade with helm image strategy", @@ -437,9 +353,9 @@ func TestHelmDeploy(t *testing.T) { return false } - expected := map[string]bool{fmt.Sprintf("image.repository=%s,image.tag=%s", dockerRef.BaseName, dockerRef.Tag): true} + expected := fmt.Sprintf("image.repository=%s,image.tag=%s", dockerRef.BaseName, dockerRef.Tag) for _, arg := range cmd.Args { - if expected[arg] { + if expected == arg { return true } } @@ -447,41 +363,42 @@ func TestHelmDeploy(t *testing.T) { }, upgradeResult: fmt.Errorf("should not have called upgrade"), }, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployHelmStyleConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeployHelmStyleConfig, false), + builds: testBuilds, }, { - description: "get success should upgrade not install", + description: "get success should upgrade by force, not install", cmd: &MockHelm{ - t: t, + t: t, + upgradeMatcher: func(cmd *exec.Cmd) bool { + for _, arg := range cmd.Args { + if arg == "--force" { + return true + } + } + return false + }, installResult: fmt.Errorf("should not have called install"), }, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, + runContext: makeRunContext(testDeployConfig, true), + builds: testBuilds, + }, + { + description: "get success should upgrade without force, not install", + cmd: &MockHelm{ + t: t, + upgradeMatcher: func(cmd *exec.Cmd) bool { + for _, arg := range cmd.Args { + if arg == "--force" { + return false + } + } + return true }, + installResult: fmt.Errorf("should not have called install"), }, - builds: testBuilds, + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, }, { description: "deploy error", @@ -489,21 +406,9 @@ func TestHelmDeploy(t *testing.T) { t: t, upgradeResult: fmt.Errorf("unexpected error"), }, - shouldErr: true, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + shouldErr: true, + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, }, { description: "dep build error", @@ -511,21 +416,9 @@ func TestHelmDeploy(t *testing.T) { t: t, depResult: fmt.Errorf("unexpected error"), }, - shouldErr: true, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployConfig, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + shouldErr: true, + runContext: makeRunContext(testDeployConfig, false), + builds: testBuilds, }, { description: "should package chart and deploy", @@ -533,21 +426,9 @@ func TestHelmDeploy(t *testing.T) { t: t, packageOut: bytes.NewBufferString("Packaged to " + os.TempDir() + "foo-0.1.2.tgz"), }, - shouldErr: false, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployFooWithPackaged, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuildsFoo, + shouldErr: false, + runContext: makeRunContext(testDeployFooWithPackaged, false), + builds: testBuildsFoo, }, { description: "should fail to deploy when packaging fails", @@ -555,39 +436,15 @@ func TestHelmDeploy(t *testing.T) { t: t, packageResult: fmt.Errorf("packaging failed"), }, - shouldErr: true, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployFooWithPackaged, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuildsFoo, + shouldErr: true, + runContext: makeRunContext(testDeployFooWithPackaged, false), + builds: testBuildsFoo, }, { description: "deploy and get templated release name", cmd: &MockHelm{t: t}, - runContext: &runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: testDeployWithTemplatedName, - }, - }, - }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }, - builds: testBuilds, + runContext: makeRunContext(testDeployWithTemplatedName, false), + builds: testBuilds, }, } @@ -741,31 +598,18 @@ func TestHelmDependencies(t *testing.T) { for _, file := range tt.files { folder.Write(file, "") } - - deployer := NewHelmDeployer(&runcontext.RunContext{ - Cfg: &latest.Pipeline{ - Deploy: latest.DeployConfig{ - DeployType: latest.DeployType{ - HelmDeploy: &latest.HelmDeploy{ - Releases: []latest.HelmRelease{ - { - Name: "skaffold-helm", - ChartPath: folder.Root(), - ValuesFiles: tt.valuesFiles, - Values: map[string]string{"image": "skaffold-helm"}, - Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, - SetValues: map[string]string{"some.key": "somevalue"}, - }, - }, - }, - }, + deployer := NewHelmDeployer(makeRunContext(&latest.HelmDeploy{ + Releases: []latest.HelmRelease{ + { + Name: "skaffold-helm", + ChartPath: folder.Root(), + ValuesFiles: tt.valuesFiles, + Values: map[string]string{"image": "skaffold-helm"}, + Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}}, + SetValues: map[string]string{"some.key": "somevalue"}, }, }, - KubeContext: testKubeContext, - Opts: &config.SkaffoldOptions{ - Namespace: testNamespace, - }, - }) + }, false)) deps, err := deployer.Dependencies() @@ -773,3 +617,20 @@ func TestHelmDependencies(t *testing.T) { }) } } + +func makeRunContext(helmDeploy *latest.HelmDeploy, force bool) *runcontext.RunContext { + return &runcontext.RunContext{ + Cfg: &latest.Pipeline{ + Deploy: latest.DeployConfig{ + DeployType: latest.DeployType{ + HelmDeploy: helmDeploy, + }, + }, + }, + KubeContext: testKubeContext, + Opts: &config.SkaffoldOptions{ + Namespace: testNamespace, + Force: force, + }, + } +} diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl.go index 80228facaa7..836b545b244 100644 --- a/pkg/skaffold/deploy/kubectl.go +++ b/pkg/skaffold/deploy/kubectl.go @@ -52,6 +52,7 @@ func NewKubectlDeployer(runCtx *runcontext.RunContext) *KubectlDeployer { Namespace: runCtx.Opts.Namespace, KubeContext: runCtx.KubeContext, Flags: runCtx.Cfg.Deploy.KubectlDeploy.Flags, + ForceDeploy: runCtx.Opts.ForceDeploy(), }, defaultRepo: runCtx.DefaultRepo, insecureRegistries: runCtx.InsecureRegistries, diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index 17903a5ff06..299256bf21f 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -36,6 +36,7 @@ type CLI struct { version ClientVersion versionOnce sync.Once + ForceDeploy bool previousApply ManifestList } @@ -59,8 +60,12 @@ func (c *CLI) Apply(ctx context.Context, out io.Writer, manifests ManifestList) return nil } - // Add --force flag to delete and redeploy image if changes can't be applied - if err := c.Run(ctx, updated.Reader(), out, "apply", c.Flags.Apply, "--force", "-f", "-"); err != nil { + args := []string{"-f", "-"} + if c.ForceDeploy { + args = append(args, "--force") + } + + if err := c.Run(ctx, updated.Reader(), out, "apply", c.Flags.Apply, args...); err != nil { return errors.Wrap(err, "kubectl apply") } diff --git a/pkg/skaffold/deploy/kubectl_test.go b/pkg/skaffold/deploy/kubectl_test.go index 59dc72eecc6..61dbe452583 100644 --- a/pkg/skaffold/deploy/kubectl_test.go +++ b/pkg/skaffold/deploy/kubectl_test.go @@ -67,6 +67,7 @@ func TestKubectlDeploy(t *testing.T) { builds []build.Artifact command util.Command shouldErr bool + forceDeploy bool }{ { description: "no manifest", @@ -87,6 +88,21 @@ func TestKubectlDeploy(t *testing.T) { }, command: testutil.NewFakeCmd(t).WithRunOut("kubectl version --client -ojson", kubectlVersion), }, + { + description: "deploy success (forced)", + cfg: &latest.KubectlDeploy{ + Manifests: []string{"deployment.yaml"}, + }, + command: testutil.NewFakeCmd(t). + WithRunOut("kubectl version --client -ojson", kubectlVersion). + WithRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), deploymentWebYAML). + WithRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force"), + builds: []build.Artifact{{ + ImageName: "leeroy-web", + Tag: "leeroy-web:123", + }}, + forceDeploy: true, + }, { description: "deploy success", cfg: &latest.KubectlDeploy{ @@ -95,7 +111,7 @@ func TestKubectlDeploy(t *testing.T) { command: testutil.NewFakeCmd(t). WithRunOut("kubectl version --client -ojson", kubectlVersion). WithRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), deploymentWebYAML). - WithRun("kubectl --context kubecontext --namespace testNamespace apply --force -f -"), + WithRun("kubectl --context kubecontext --namespace testNamespace apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", Tag: "leeroy-web:123", @@ -109,7 +125,7 @@ func TestKubectlDeploy(t *testing.T) { command: testutil.NewFakeCmd(t). WithRunOut("kubectl version --client -ojson", kubectlVersion). WithRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), deploymentWebYAML). - WithRunErr("kubectl --context kubecontext --namespace testNamespace apply --force -f -", fmt.Errorf("")), + WithRunErr("kubectl --context kubecontext --namespace testNamespace apply -f -", fmt.Errorf("")), builds: []build.Artifact{{ ImageName: "leeroy-web", Tag: "leeroy-web:123", @@ -129,7 +145,7 @@ func TestKubectlDeploy(t *testing.T) { command: testutil.NewFakeCmd(t). WithRunOut("kubectl version --client -ojson", kubectlVersion). WithRunOut("kubectl --context kubecontext --namespace testNamespace -v=0 create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), deploymentWebYAML). - WithRunErr("kubectl --context kubecontext --namespace testNamespace -v=0 apply --overwrite=true --force -f -", fmt.Errorf("")), + WithRunErr("kubectl --context kubecontext --namespace testNamespace -v=0 apply --overwrite=true -f -", fmt.Errorf("")), builds: []build.Artifact{{ ImageName: "leeroy-web", Tag: "leeroy-web:123", @@ -155,6 +171,7 @@ func TestKubectlDeploy(t *testing.T) { KubeContext: testKubeContext, Opts: &config.SkaffoldOptions{ Namespace: testNamespace, + Force: test.forceDeploy, }, }) @@ -248,7 +265,7 @@ func TestKubectlRedeploy(t *testing.T) { util.DefaultExecCommand = testutil.NewFakeCmd(t). WithRunOut("kubectl version --client -ojson", kubectlVersion). WithRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML). - WithRunInput("kubectl --context kubecontext --namespace testNamespace apply --force -f -", `apiVersion: v1 + WithRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", `apiVersion: v1 kind: Pod metadata: labels: @@ -270,7 +287,7 @@ spec: - image: leeroy-web:v1 name: leeroy-web`). WithRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML). - WithRunInput("kubectl --context kubecontext --namespace testNamespace apply --force -f -", `apiVersion: v1 + WithRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", `apiVersion: v1 kind: Pod metadata: labels: diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go index bb2ce2c6113..27136554bd0 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize.go @@ -74,6 +74,7 @@ func NewKustomizeDeployer(runCtx *runcontext.RunContext) *KustomizeDeployer { Namespace: runCtx.Opts.Namespace, KubeContext: runCtx.KubeContext, Flags: runCtx.Cfg.Deploy.KustomizeDeploy.Flags, + ForceDeploy: runCtx.Opts.ForceDeploy(), }, defaultRepo: runCtx.DefaultRepo, } diff --git a/pkg/skaffold/deploy/kustomize_test.go b/pkg/skaffold/deploy/kustomize_test.go index 2b07d4deb95..a948902911c 100644 --- a/pkg/skaffold/deploy/kustomize_test.go +++ b/pkg/skaffold/deploy/kustomize_test.go @@ -40,6 +40,7 @@ func TestKustomizeDeploy(t *testing.T) { builds []build.Artifact command util.Command shouldErr bool + forceDeploy bool }{ { description: "no manifest", @@ -58,11 +59,12 @@ func TestKustomizeDeploy(t *testing.T) { command: testutil.NewFakeCmd(t). WithRunOut("kubectl version --client -ojson", kubectlVersion). WithRunOut("kustomize build "+tmpDir.Root(), deploymentWebYAML). - WithRun("kubectl --context kubecontext --namespace testNamespace apply --force -f -"), + WithRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force"), builds: []build.Artifact{{ ImageName: "leeroy-web", Tag: "leeroy-web:123", }}, + forceDeploy: true, }, } @@ -83,6 +85,7 @@ func TestKustomizeDeploy(t *testing.T) { KubeContext: testKubeContext, Opts: &config.SkaffoldOptions{ Namespace: testNamespace, + Force: test.forceDeploy, }, }) err := k.Deploy(context.Background(), ioutil.Discard, test.builds, nil)