diff --git a/docs/content/en/docs/environment/templating.md b/docs/content/en/docs/environment/templating.md
index 6e43de8fceb..a8525824651 100644
--- a/docs/content/en/docs/environment/templating.md
+++ b/docs/content/en/docs/environment/templating.md
@@ -19,6 +19,9 @@ List of fields that support templating:
* `build.tagPolicy.envTemplate.template` (see [envTemplate tagger]({{< relref "/docs/pipeline-stages/taggers#envtemplate-using-values-of-environment-variables-as-tags)" >}}))
* `deploy.helm.releases.setValueTemplates` (see [Deploying with helm]({{< relref "/docs/pipeline-stages/deployers#deploying-with-helm)" >}}))
* `deploy.helm.releases.name` (see [Deploying with helm]({{< relref "/docs/pipeline-stages/deployers#deploying-with-helm)" >}}))
+* `deploy.helm.releases.namespace` (see [Deploying with helm]({{< relref "/docs/pipeline-stages/deployers#deploying-with-helm)" >}}))
+* `deploy.kubectl.defaultNamespace`
+* `deploy.kustomize.defaultNamespace`
_Please note, this list is not exhaustive._
diff --git a/docs/content/en/schemas/v2beta8.json b/docs/content/en/schemas/v2beta8.json
index 27c22d6f4c9..76992767b61 100755
--- a/docs/content/en/schemas/v2beta8.json
+++ b/docs/content/en/schemas/v2beta8.json
@@ -1751,6 +1751,11 @@
},
"KubectlDeploy": {
"properties": {
+ "defaultNamespace": {
+ "type": "string",
+ "description": "default namespace passed to kubectl on deployment if no other override is given.",
+ "x-intellij-html-description": "default namespace passed to kubectl on deployment if no other override is given."
+ },
"flags": {
"$ref": "#/definitions/KubectlFlags",
"description": "additional flags passed to `kubectl`.",
@@ -1778,7 +1783,8 @@
"preferredOrder": [
"manifests",
"remoteManifests",
- "flags"
+ "flags",
+ "defaultNamespace"
],
"additionalProperties": false,
"description": "*beta* uses a client side `kubectl apply` to deploy manifests. You'll need a `kubectl` CLI version installed that's compatible with your cluster.",
@@ -1841,6 +1847,11 @@
"x-intellij-html-description": "additional args passed to kustomize build
.",
"default": "[]"
},
+ "defaultNamespace": {
+ "type": "string",
+ "description": "default namespace passed to kubectl on deployment if no other override is given.",
+ "x-intellij-html-description": "default namespace passed to kubectl on deployment if no other override is given."
+ },
"flags": {
"$ref": "#/definitions/KubectlFlags",
"description": "additional flags passed to `kubectl`.",
@@ -1859,7 +1870,8 @@
"preferredOrder": [
"paths",
"flags",
- "buildArgs"
+ "buildArgs",
+ "defaultNamespace"
],
"additionalProperties": false,
"description": "*beta* uses the `kustomize` CLI to \"patch\" a deployment for a target environment.",
diff --git a/integration/port_forward_test.go b/integration/port_forward_test.go
index 381282e273b..c545fa68e6e 100644
--- a/integration/port_forward_test.go
+++ b/integration/port_forward_test.go
@@ -46,7 +46,7 @@ func TestPortForward(t *testing.T) {
Opts: config.SkaffoldOptions{
Namespace: ns.Name,
},
- })
+ }, "")
logrus.SetLevel(logrus.TraceLevel)
portforward.SimulateDevCycle(t, kubectlCLI, ns.Name)
diff --git a/integration/render_test.go b/integration/render_test.go
index 29a2767d29f..a330351dc66 100644
--- a/integration/render_test.go
+++ b/integration/render_test.go
@@ -30,6 +30,7 @@ import (
"github.com/GoogleContainerTools/skaffold/integration/skaffold"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
+ "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
@@ -75,7 +76,7 @@ spec:
t.NewTempDir().
Write("deployment.yaml", test.input).
Chdir()
- deployer := deploy.NewKubectlDeployer(&runcontext.RunContext{
+ deployer, err := deploy.NewKubectlDeployer(&runcontext.RunContext{
WorkingDir: ".",
Cfg: latest.Pipeline{
Deploy: latest.DeployConfig{
@@ -87,8 +88,9 @@ spec:
},
},
}, nil)
+ t.RequireNoError(err)
var b bytes.Buffer
- err := deployer.Render(context.Background(), &b, test.builds, false, test.renderPath)
+ err = deployer.Render(context.Background(), &b, test.builds, false, test.renderPath)
t.CheckNoError(err)
dat, err := ioutil.ReadFile(test.renderPath)
@@ -229,7 +231,7 @@ spec:
Write("deployment.yaml", test.input).
Chdir()
- deployer := deploy.NewKubectlDeployer(&runcontext.RunContext{
+ deployer, err := deploy.NewKubectlDeployer(&runcontext.RunContext{
WorkingDir: ".",
Cfg: latest.Pipeline{
Deploy: latest.DeployConfig{
@@ -240,9 +242,13 @@ spec:
},
},
},
+ Opts: config.SkaffoldOptions{
+ AddSkaffoldLabels: true,
+ },
}, nil)
+ t.RequireNoError(err)
var b bytes.Buffer
- err := deployer.Render(context.Background(), &b, test.builds, false, "")
+ err = deployer.Render(context.Background(), &b, test.builds, false, "")
t.CheckNoError(err)
t.CheckDeepEqual(test.expectedOut, b.String())
diff --git a/pkg/skaffold/build/cluster/types.go b/pkg/skaffold/build/cluster/types.go
index 149c89c6278..fcc3f1f212e 100644
--- a/pkg/skaffold/build/cluster/types.go
+++ b/pkg/skaffold/build/cluster/types.go
@@ -58,7 +58,7 @@ func NewBuilder(cfg Config) (*Builder, error) {
return &Builder{
ClusterDetails: cfg.Pipeline().Build.Cluster,
- kubectlcli: kubectl.NewCLI(cfg),
+ kubectlcli: kubectl.NewCLI(cfg, ""),
timeout: timeout,
kubeContext: cfg.GetKubeContext(),
insecureRegistries: cfg.GetInsecureRegistries(),
diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm.go
index bdfd566b6ad..9b27590c655 100644
--- a/pkg/skaffold/deploy/helm.go
+++ b/pkg/skaffold/deploy/helm.go
@@ -115,7 +115,13 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build
// collect namespaces
for _, r := range results {
- if trimmed := strings.TrimSpace(r.Namespace); trimmed != "" {
+ var namespace string
+ namespace, err = util.ExpandEnvTemplate(r.Namespace, nil)
+ if err != nil {
+ return nil, fmt.Errorf("cannot parse the release namespace template: %w", err)
+ }
+
+ if trimmed := strings.TrimSpace(namespace); trimmed != "" {
nsMap[trimmed] = struct{}{}
}
}
@@ -223,7 +229,10 @@ func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
if h.namespace != "" {
namespace = h.namespace
} else if r.Namespace != "" {
- namespace = r.Namespace
+ namespace, err = util.ExpandEnvTemplate(r.Namespace, nil)
+ if err != nil {
+ return fmt.Errorf("cannot parse the release namespace template: %w", err)
+ }
}
args := []string{"delete", releaseName}
@@ -286,7 +295,13 @@ func (h *HelmDeployer) Render(ctx context.Context, out io.Writer, builds []build
}
if r.Namespace != "" {
- args = append(args, "--namespace", r.Namespace)
+ var namespace string
+ namespace, err = util.ExpandEnvTemplate(r.Namespace, nil)
+ if err != nil {
+ return fmt.Errorf("cannot parse the release namespace template: %w", err)
+ }
+
+ args = append(args, "--namespace", namespace)
}
outBuffer := new(bytes.Buffer)
@@ -340,7 +355,10 @@ func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r lates
if h.namespace != "" {
opts.namespace = h.namespace
} else if r.Namespace != "" {
- opts.namespace = r.Namespace
+ opts.namespace, err = util.ExpandEnvTemplate(r.Namespace, nil)
+ if err != nil {
+ return nil, fmt.Errorf("cannot parse the release namespace template: %w", err)
+ }
}
if err := h.exec(ctx, ioutil.Discard, false, getArgs(helmVersion, releaseName, opts.namespace)...); err != nil {
diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm_test.go
index 7ff8f2b758b..7498b69a3fd 100644
--- a/pkg/skaffold/deploy/helm_test.go
+++ b/pkg/skaffold/deploy/helm_test.go
@@ -73,6 +73,21 @@ var testDeployNamespacedConfig = latest.HelmDeploy{
}},
}
+var testDeployEnvTemplateNamespacedConfig = latest.HelmDeploy{
+ Releases: []latest.HelmRelease{{
+ Name: "skaffold-helm",
+ ChartPath: "examples/test",
+ ArtifactOverrides: map[string]string{
+ "image": "skaffold-helm",
+ },
+ Overrides: schemautil.HelmOverrides{Values: map[string]interface{}{"foo": "bar"}},
+ SetValues: map[string]string{
+ "some.key": "somevalue",
+ },
+ Namespace: "testRelease{{.FOO}}Namespace",
+ }},
+}
+
var testDeployConfigTemplated = latest.HelmDeploy{
Releases: []latest.HelmRelease{{
Name: "skaffold-helm",
@@ -293,6 +308,7 @@ var testDeployCreateNamespaceConfig = latest.HelmDeploy{
}
var testNamespace = "testNamespace"
+var testNamespace2 = "testNamespace2"
var validDeployYaml = `
# Source: skaffold-helm/templates/deployment.yaml
@@ -435,6 +451,7 @@ func TestHelmDeploy(t *testing.T) {
force bool
shouldErr bool
expectedWarnings []string
+ envs map[string]string
}{
{
description: "deploy success",
@@ -492,6 +509,17 @@ func TestHelmDeploy(t *testing.T) {
helm: testDeployNamespacedConfig,
builds: testBuilds,
},
+ {
+ description: "helm3.0 namespaced (with env template) deploy success",
+ commands: testutil.
+ CmdRunWithOutput("helm version --client", version30).
+ AndRun("helm --kube-context kubecontext get all --namespace testReleaseFOOBARNamespace skaffold-helm --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testReleaseFOOBARNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext get all --namespace testReleaseFOOBARNamespace skaffold-helm --kubeconfig kubeconfig"),
+ helm: testDeployEnvTemplateNamespacedConfig,
+ builds: testBuilds,
+ },
{
description: "helm3.0 namespaced context deploy success",
commands: testutil.
@@ -538,6 +566,17 @@ func TestHelmDeploy(t *testing.T) {
helm: testDeployNamespacedConfig,
builds: testBuilds,
},
+ {
+ description: "helm3.1 namespaced deploy (with env template) success",
+ commands: testutil.
+ CmdRunWithOutput("helm version --client", version31).
+ AndRun("helm --kube-context kubecontext get all --namespace testReleaseFOOBARNamespace skaffold-helm --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testReleaseFOOBARNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig").
+ AndRun("helm --kube-context kubecontext get all --namespace testReleaseFOOBARNamespace skaffold-helm --kubeconfig kubeconfig"),
+ helm: testDeployEnvTemplateNamespacedConfig,
+ builds: testBuilds,
+ },
{
description: "helm3.1 namespaced context deploy success",
commands: testutil.
@@ -908,6 +947,7 @@ func TestHelmCleanup(t *testing.T) {
builds []build.Artifact
shouldErr bool
expectedWarnings []string
+ envs map[string]string
}{
{
description: "cleanup success",
@@ -933,6 +973,14 @@ func TestHelmCleanup(t *testing.T) {
helm: testDeployNamespacedConfig,
builds: testBuilds,
},
+ {
+ description: "helm3 namespace (with env template) cleanup success",
+ commands: testutil.
+ CmdRunWithOutput("helm version --client", version31).
+ AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testReleaseFOOBARNamespace --kubeconfig kubeconfig"),
+ helm: testDeployEnvTemplateNamespacedConfig,
+ builds: testBuilds,
+ },
{
description: "helm3 namespaced context cleanup success",
commands: testutil.
@@ -1163,6 +1211,7 @@ func TestHelmRender(t *testing.T) {
outputFile string
expected string
builds []build.Artifact
+ envs map[string]string
}{
{
description: "error if version can't be retrieved",
@@ -1217,7 +1266,7 @@ func TestHelmRender(t *testing.T) {
shouldErr: false,
commands: testutil.
CmdRunWithOutput("helm version --client", version31).
- AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set image.name=skaffold-helm --set image.tag=skaffold-helm:tag1 --set missing.key= --set other.key= --set some.key=somevalue --kubeconfig kubeconfig"),
+ AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set image.name=skaffold-helm --set image.tag=skaffold-helm:tag1 --set missing.key= --set other.key=FOOBAR --set some.key=somevalue --kubeconfig kubeconfig"),
helm: testDeployConfigTemplated,
builds: []build.Artifact{
{
@@ -1237,6 +1286,18 @@ func TestHelmRender(t *testing.T) {
Tag: "skaffold-helm:tag1",
}},
},
+ {
+ description: "render with namespace",
+ shouldErr: false,
+ commands: testutil.CmdRunWithOutput("helm version --client", version31).
+ AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --set-string image=skaffold-helm:tag1 --set some.key=somevalue --namespace testReleaseFOOBARNamespace --kubeconfig kubeconfig"),
+ helm: testDeployEnvTemplateNamespacedConfig,
+ builds: []build.Artifact{
+ {
+ ImageName: "skaffold-helm",
+ Tag: "skaffold-helm:tag1",
+ }},
+ },
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
@@ -1245,6 +1306,8 @@ func TestHelmRender(t *testing.T) {
file = t.NewTempDir().Path(test.outputFile)
}
+ t.Override(&util.OSEnviron, func() []string { return []string{"FOO=FOOBAR"} })
+
deployer := NewHelmDeployer(&helmConfig{
helm: test.helm,
}, nil)
diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl.go
index 255a7ea47fb..8da56963b4e 100644
--- a/pkg/skaffold/deploy/kubectl.go
+++ b/pkg/skaffold/deploy/kubectl.go
@@ -67,17 +67,26 @@ type Config interface {
// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled
// with the needed configuration for `kubectl apply`
-func NewKubectlDeployer(cfg Config, labels map[string]string) *KubectlDeployer {
+func NewKubectlDeployer(cfg Config, labels map[string]string) (*KubectlDeployer, error) {
+ defaultNamespace := ""
+ if cfg.Pipeline().Deploy.KubectlDeploy.DefaultNamespace != nil {
+ var err error
+ defaultNamespace, err = util.ExpandEnvTemplate(*cfg.Pipeline().Deploy.KubectlDeploy.DefaultNamespace, nil)
+ if err != nil {
+ return nil, err
+ }
+ }
+
return &KubectlDeployer{
KubectlDeploy: cfg.Pipeline().Deploy.KubectlDeploy,
workingDir: cfg.GetWorkingDir(),
globalConfig: cfg.GlobalConfig(),
defaultRepo: cfg.DefaultRepo(),
- kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KubectlDeploy.Flags),
+ kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KubectlDeploy.Flags, defaultNamespace),
insecureRegistries: cfg.GetInsecureRegistries(),
skipRender: cfg.SkipRender(),
labels: labels,
- }
+ }, nil
}
// Deploy templates the provided manifests with a simple `find and replace` and
diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go
index ef394097b79..b326da9d75f 100644
--- a/pkg/skaffold/deploy/kubectl/cli.go
+++ b/pkg/skaffold/deploy/kubectl/cli.go
@@ -47,9 +47,9 @@ type Config interface {
WaitForDeletions() config.WaitForDeletions
}
-func NewCLI(cfg Config, flags latest.KubectlFlags) CLI {
+func NewCLI(cfg Config, flags latest.KubectlFlags, defaultNameSpace string) CLI {
return CLI{
- CLI: pkgkubectl.NewCLI(cfg),
+ CLI: pkgkubectl.NewCLI(cfg, defaultNameSpace),
Flags: flags,
forceDeploy: cfg.ForceDeploy(),
waitForDeletions: cfg.WaitForDeletions(),
diff --git a/pkg/skaffold/deploy/kubectl_test.go b/pkg/skaffold/deploy/kubectl_test.go
index 393c57f55f7..2b9e762676e 100644
--- a/pkg/skaffold/deploy/kubectl_test.go
+++ b/pkg/skaffold/deploy/kubectl_test.go
@@ -42,6 +42,8 @@ const (
kubectlVersion118 = `{"clientVersion":{"major":"1","minor":"18"}}`
)
+var testNamespace2FromEnvTemplate = "test{{.MYENV}}ace2" // needs `MYENV=Namesp` environment variable
+
const deploymentWebYAML = `apiVersion: v1
kind: Pod
metadata:
@@ -89,13 +91,15 @@ spec:
func TestKubectlDeploy(t *testing.T) {
tests := []struct {
- description string
- kubectl latest.KubectlDeploy
- builds []build.Artifact
- commands util.Command
- shouldErr bool
- forceDeploy bool
- waitForDeletions bool
+ description string
+ kubectl latest.KubectlDeploy
+ builds []build.Artifact
+ commands util.Command
+ shouldErr bool
+ forceDeploy bool
+ waitForDeletions bool
+ skipSkaffoldNamespaceOption bool
+ envs map[string]string
}{
{
description: "no manifest",
@@ -171,6 +175,45 @@ func TestKubectlDeploy(t *testing.T) {
}},
waitForDeletions: true,
},
+ {
+ description: "deploy success (default namespace)",
+ kubectl: latest.KubectlDeploy{
+ Manifests: []string{"deployment.yaml"},
+ DefaultNamespace: &testNamespace2,
+ },
+ commands: testutil.
+ CmdRunOut("kubectl version --client -ojson", kubectlVersion118).
+ AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
+ AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -"),
+ builds: []build.Artifact{{
+ ImageName: "leeroy-web",
+ Tag: "leeroy-web:v1",
+ }},
+ waitForDeletions: true,
+ skipSkaffoldNamespaceOption: true,
+ },
+ {
+ description: "deploy success (default namespace with env template)",
+ kubectl: latest.KubectlDeploy{
+ Manifests: []string{"deployment.yaml"},
+ DefaultNamespace: &testNamespace2FromEnvTemplate,
+ },
+ commands: testutil.
+ CmdRunOut("kubectl version --client -ojson", kubectlVersion118).
+ AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
+ AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -"),
+ builds: []build.Artifact{{
+ ImageName: "leeroy-web",
+ Tag: "leeroy-web:v1",
+ }},
+ waitForDeletions: true,
+ skipSkaffoldNamespaceOption: true,
+ envs: map[string]string{
+ "MYENV": "Namesp",
+ },
+ },
{
description: "http manifest",
kubectl: latest.KubectlDeploy{
@@ -179,11 +222,13 @@ func TestKubectlDeploy(t *testing.T) {
commands: testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml -f http://remote.yaml", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"),
builds: []build.Artifact{{
ImageName: "leeroy-web",
Tag: "leeroy-web:v1",
}},
+ waitForDeletions: true,
},
{
description: "deploy command error",
@@ -193,12 +238,14 @@ func TestKubectlDeploy(t *testing.T) {
commands: testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -f -", fmt.Errorf("")),
builds: []build.Artifact{{
ImageName: "leeroy-web",
Tag: "leeroy-web:v1",
}},
- shouldErr: true,
+ shouldErr: true,
+ waitForDeletions: true,
},
{
description: "additional flags",
@@ -219,19 +266,25 @@ func TestKubectlDeploy(t *testing.T) {
ImageName: "leeroy-web",
Tag: "leeroy-web:v1",
}},
- waitForDeletions: true,
shouldErr: true,
+ waitForDeletions: true,
},
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
+ t.SetEnvs(test.envs)
t.Override(&util.DefaultExecCommand, test.commands)
t.NewTempDir().
Write("deployment.yaml", deploymentWebYAML).
Touch("empty.ignored").
Chdir()
- k := NewKubectlDeployer(&kubectlConfig{
+ skaffoldNamespaceOption := ""
+ if !test.skipSkaffoldNamespaceOption {
+ skaffoldNamespaceOption = testNamespace
+ }
+
+ k, err := NewKubectlDeployer(&kubectlConfig{
workingDir: ".",
kubectl: test.kubectl,
force: test.forceDeploy,
@@ -240,9 +293,12 @@ func TestKubectlDeploy(t *testing.T) {
Delay: 0 * time.Second,
Max: 10 * time.Second,
},
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{
+ Namespace: skaffoldNamespaceOption}},
}, nil)
+ t.RequireNoError(err)
- _, err := k.Deploy(context.Background(), ioutil.Discard, test.builds)
+ _, err = k.Deploy(context.Background(), ioutil.Discard, test.builds)
t.CheckError(test.shouldErr, err)
})
@@ -310,11 +366,14 @@ func TestKubectlCleanup(t *testing.T) {
Write("deployment.yaml", deploymentWebYAML).
Chdir()
- k := NewKubectlDeployer(&kubectlConfig{
+ k, err := NewKubectlDeployer(&kubectlConfig{
workingDir: ".",
kubectl: test.kubectl,
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}},
}, nil)
- err := k.Cleanup(context.Background(), ioutil.Discard)
+ t.RequireNoError(err)
+
+ err = k.Cleanup(context.Background(), ioutil.Discard)
t.CheckError(test.shouldErr, err)
})
@@ -355,11 +414,14 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) {
Write("deployment.yaml", deploymentWebYAML).
Chdir()
- k := NewKubectlDeployer(&kubectlConfig{
+ k, err := NewKubectlDeployer(&kubectlConfig{
workingDir: ".",
kubectl: test.kubectl,
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}},
}, nil)
- err := k.Cleanup(context.Background(), ioutil.Discard)
+ t.RequireNoError(err)
+
+ err = k.Cleanup(context.Background(), ioutil.Discard)
t.CheckNoError(err)
})
@@ -374,30 +436,29 @@ func TestKubectlRedeploy(t *testing.T) {
t.Override(&util.DefaultExecCommand, testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
- AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1, "").
- AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1).
- AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, "").
- AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentAppYAMLv2).
- AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, ""),
+ AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1, "").
+ AndRunInput("kubectl --context kubecontext apply -f -", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1).
+ AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, "").
+ AndRunInput("kubectl --context kubecontext apply -f -", deploymentAppYAMLv2).
+ AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, ""),
)
- deployer := NewKubectlDeployer(&kubectlConfig{
- workingDir: tmpDir.Root(),
+ deployer, err := NewKubectlDeployer(&kubectlConfig{
+ workingDir: ".",
kubectl: latest.KubectlDeploy{
- Manifests: []string{tmpDir.Path("deployment-app.yaml"), tmpDir.Path("deployment-web.yaml")},
- },
+ Manifests: []string{tmpDir.Path("deployment-app.yaml"), tmpDir.Path("deployment-web.yaml")}},
waitForDeletions: config.WaitForDeletions{
Enabled: true,
Delay: 0 * time.Millisecond,
- Max: 10 * time.Second,
- },
+ Max: 10 * time.Second},
}, nil)
+ t.RequireNoError(err)
// Deploy one manifest
- _, err := deployer.Deploy(context.Background(), ioutil.Discard, []build.Artifact{
+ _, err = deployer.Deploy(context.Background(), ioutil.Discard, []build.Artifact{
{ImageName: "leeroy-web", Tag: "leeroy-web:v1"},
{ImageName: "leeroy-app", Tag: "leeroy-app:v1"},
})
@@ -425,32 +486,32 @@ func TestKubectlWaitForDeletions(t *testing.T) {
t.Override(&util.DefaultExecCommand, testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
- AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
+ AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
"items":[
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}},
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}},
{"metadata":{"name":"leeroy-front"}}
]
}`).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
"items":[
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}},
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}},
{"metadata":{"name":"leeroy-front"}}
]
}`).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
"items":[
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}},
{"metadata":{"name":"leeroy-front"}}
]
}`).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
- AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentWebYAMLv1),
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
+ AndRunInput("kubectl --context kubecontext apply -f -", deploymentWebYAMLv1),
)
- deployer := NewKubectlDeployer(&kubectlConfig{
+ deployer, err := NewKubectlDeployer(&kubectlConfig{
workingDir: tmpDir.Root(),
kubectl: latest.KubectlDeploy{
Manifests: []string{tmpDir.Path("deployment-web.yaml")},
@@ -461,9 +522,10 @@ func TestKubectlWaitForDeletions(t *testing.T) {
Max: 10 * time.Second,
},
}, nil)
+ t.RequireNoError(err)
var out bytes.Buffer
- _, err := deployer.Deploy(context.Background(), &out, []build.Artifact{
+ _, err = deployer.Deploy(context.Background(), &out, []build.Artifact{
{ImageName: "leeroy-web", Tag: "leeroy-web:v1"},
})
@@ -480,8 +542,8 @@ func TestKubectlWaitForDeletionsFails(t *testing.T) {
t.Override(&util.DefaultExecCommand, testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
- AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML).
- AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
+ AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{
"items":[
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}},
{"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}}
@@ -489,7 +551,7 @@ func TestKubectlWaitForDeletionsFails(t *testing.T) {
}`),
)
- deployer := NewKubectlDeployer(&kubectlConfig{
+ deployer, err := NewKubectlDeployer(&kubectlConfig{
workingDir: tmpDir.Root(),
kubectl: latest.KubectlDeploy{
Manifests: []string{tmpDir.Path("deployment-web.yaml")},
@@ -500,8 +562,9 @@ func TestKubectlWaitForDeletionsFails(t *testing.T) {
Max: 100 * time.Millisecond,
},
}, nil)
+ t.RequireNoError(err)
- _, err := deployer.Deploy(context.Background(), ioutil.Discard, []build.Artifact{
+ _, err = deployer.Deploy(context.Background(), ioutil.Discard, []build.Artifact{
{ImageName: "leeroy-web", Tag: "leeroy-web:v1"},
})
@@ -559,11 +622,13 @@ func TestDependencies(t *testing.T) {
Touch("00/b.yaml", "00/a.yaml").
Chdir()
- k := NewKubectlDeployer(&kubectlConfig{
+ k, err := NewKubectlDeployer(&kubectlConfig{
kubectl: latest.KubectlDeploy{
Manifests: test.manifests,
},
}, nil)
+ t.RequireNoError(err)
+
dependencies, err := k.Dependencies()
t.CheckNoError(err)
@@ -674,17 +739,16 @@ spec:
t.Override(&util.DefaultExecCommand, testutil.
CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), test.input))
-
- deployer := NewKubectlDeployer(&kubectlConfig{
+ deployer, err := NewKubectlDeployer(&kubectlConfig{
workingDir: ".",
defaultRepo: "gcr.io/project",
kubectl: latest.KubectlDeploy{
Manifests: []string{tmpDir.Path("deployment.yaml")},
},
}, nil)
+ t.RequireNoError(err)
var b bytes.Buffer
- err := deployer.Render(context.Background(), &b, test.builds, true, "")
-
+ err = deployer.Render(context.Background(), &b, test.builds, true, "")
t.CheckNoError(err)
t.CheckDeepEqual(test.expected, b.String())
})
@@ -720,14 +784,15 @@ func TestGCSManifests(t *testing.T) {
if err := ioutil.WriteFile(manifestTmpDir+"/deployment.yaml", []byte(deploymentWebYAML), os.ModePerm); err != nil {
t.Fatal(err)
}
-
- k := NewKubectlDeployer(&kubectlConfig{
+ k, err := NewKubectlDeployer(&kubectlConfig{
workingDir: ".",
kubectl: test.kubectl,
skipRender: test.skipRender,
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}},
}, nil)
+ t.RequireNoError(err)
- _, err := k.Deploy(context.Background(), ioutil.Discard, nil)
+ _, err = k.Deploy(context.Background(), ioutil.Discard, nil)
t.CheckError(test.shouldErr, err)
})
@@ -745,7 +810,7 @@ type kubectlConfig struct {
}
func (c *kubectlConfig) GetKubeContext() string { return testKubeContext }
-func (c *kubectlConfig) GetKubeNamespace() string { return testNamespace }
+func (c *kubectlConfig) GetKubeNamespace() string { return c.Opts.Namespace }
func (c *kubectlConfig) WorkingDir() string { return c.workingDir }
func (c *kubectlConfig) SkipRender() bool { return c.skipRender }
func (c *kubectlConfig) ForceDeploy() bool { return c.force }
diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize.go
index 4ffe8f233e3..94c4f08e5fc 100644
--- a/pkg/skaffold/deploy/kustomize.go
+++ b/pkg/skaffold/deploy/kustomize.go
@@ -99,14 +99,23 @@ type KustomizeDeployer struct {
globalConfig string
}
-func NewKustomizeDeployer(cfg Config, labels map[string]string) *KustomizeDeployer {
+func NewKustomizeDeployer(cfg Config, labels map[string]string) (*KustomizeDeployer, error) {
+ defaultNamespace := ""
+ if cfg.Pipeline().Deploy.KustomizeDeploy.DefaultNamespace != nil {
+ var err error
+ defaultNamespace, err = util.ExpandEnvTemplate(*cfg.Pipeline().Deploy.KustomizeDeploy.DefaultNamespace, nil)
+ if err != nil {
+ return nil, err
+ }
+ }
+
return &KustomizeDeployer{
KustomizeDeploy: cfg.Pipeline().Deploy.KustomizeDeploy,
- kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KustomizeDeploy.Flags),
+ kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KustomizeDeploy.Flags, defaultNamespace),
insecureRegistries: cfg.GetInsecureRegistries(),
globalConfig: cfg.GlobalConfig(),
labels: labels,
- }
+ }, nil
}
// Deploy runs `kubectl apply` on the manifest generated by kustomize.
diff --git a/pkg/skaffold/deploy/kustomize_test.go b/pkg/skaffold/deploy/kustomize_test.go
index b40d8aa9f73..b083a4e93ec 100644
--- a/pkg/skaffold/deploy/kustomize_test.go
+++ b/pkg/skaffold/deploy/kustomize_test.go
@@ -35,12 +35,14 @@ import (
func TestKustomizeDeploy(t *testing.T) {
tests := []struct {
- description string
- kustomize latest.KustomizeDeploy
- builds []build.Artifact
- commands util.Command
- shouldErr bool
- forceDeploy bool
+ description string
+ kustomize latest.KustomizeDeploy
+ builds []build.Artifact
+ commands util.Command
+ shouldErr bool
+ forceDeploy bool
+ skipSkaffoldNamespaceOption bool
+ envs map[string]string
}{
{
description: "no manifest",
@@ -67,6 +69,45 @@ func TestKustomizeDeploy(t *testing.T) {
}},
forceDeploy: true,
},
+ {
+ description: "deploy success (default namespace)",
+ kustomize: latest.KustomizeDeploy{
+ KustomizePaths: []string{"."},
+ DefaultNamespace: &testNamespace2,
+ },
+ commands: testutil.
+ CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
+ AndRunOut("kustomize build .", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
+ AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f - --force --grace-period=0"),
+ builds: []build.Artifact{{
+ ImageName: "leeroy-web",
+ Tag: "leeroy-web:v1",
+ }},
+ forceDeploy: true,
+ skipSkaffoldNamespaceOption: true,
+ },
+ {
+ description: "deploy success (default namespace with env template)",
+ kustomize: latest.KustomizeDeploy{
+ KustomizePaths: []string{"."},
+ DefaultNamespace: &testNamespace2FromEnvTemplate,
+ },
+ commands: testutil.
+ CmdRunOut("kubectl version --client -ojson", kubectlVersion112).
+ AndRunOut("kustomize build .", deploymentWebYAML).
+ AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, "").
+ AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f - --force --grace-period=0"),
+ builds: []build.Artifact{{
+ ImageName: "leeroy-web",
+ Tag: "leeroy-web:v1",
+ }},
+ forceDeploy: true,
+ skipSkaffoldNamespaceOption: true,
+ envs: map[string]string{
+ "MYENV": "Namesp",
+ },
+ },
{
description: "deploy success with multiple kustomizations",
kustomize: latest.KustomizeDeploy{
@@ -93,11 +134,17 @@ func TestKustomizeDeploy(t *testing.T) {
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
+ t.SetEnvs(test.envs)
t.Override(&util.DefaultExecCommand, test.commands)
t.NewTempDir().
Chdir()
- k := NewKustomizeDeployer(&kustomizeConfig{
+ skaffoldNamespaceOption := ""
+ if !test.skipSkaffoldNamespaceOption {
+ skaffoldNamespaceOption = testNamespace
+ }
+
+ k, err := NewKustomizeDeployer(&kustomizeConfig{
workingDir: ".",
force: test.forceDeploy,
waitForDeletions: config.WaitForDeletions{
@@ -106,8 +153,11 @@ func TestKustomizeDeploy(t *testing.T) {
Max: 10 * time.Second,
},
kustomize: test.kustomize,
- }, nil)
- _, err := k.Deploy(context.Background(), ioutil.Discard, test.builds)
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{
+ Namespace: skaffoldNamespaceOption,
+ }}}, nil)
+ t.RequireNoError(err)
+ _, err = k.Deploy(context.Background(), ioutil.Discard, test.builds)
t.CheckError(test.shouldErr, err)
})
@@ -169,11 +219,14 @@ func TestKustomizeCleanup(t *testing.T) {
testutil.Run(t, test.description, func(t *testutil.T) {
t.Override(&util.DefaultExecCommand, test.commands)
- k := NewKustomizeDeployer(&kustomizeConfig{
+ k, err := NewKustomizeDeployer(&kustomizeConfig{
workingDir: tmpDir.Root(),
kustomize: test.kustomize,
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{
+ Namespace: testNamespace}},
}, nil)
- err := k.Cleanup(context.Background(), ioutil.Discard)
+ t.RequireNoError(err)
+ err = k.Cleanup(context.Background(), ioutil.Discard)
t.CheckError(test.shouldErr, err)
})
@@ -373,11 +426,13 @@ func TestDependenciesForKustomization(t *testing.T) {
tmpDir.Write(path, contents)
}
- k := NewKustomizeDeployer(&kustomizeConfig{
+ k, err := NewKustomizeDeployer(&kustomizeConfig{
kustomize: latest.KustomizeDeploy{
KustomizePaths: kustomizePaths,
},
}, nil)
+ t.RequireNoError(err)
+
deps, err := k.Dependencies()
t.CheckErrorAndDeepEqual(test.shouldErr, err, tmpDir.Paths(test.expected...), deps)
@@ -616,15 +671,17 @@ spec:
t.Override(&util.DefaultExecCommand, fakeCmd)
t.NewTempDir().Chdir()
- k := NewKustomizeDeployer(&kustomizeConfig{
+ k, err := NewKustomizeDeployer(&kustomizeConfig{
workingDir: ".",
kustomize: latest.KustomizeDeploy{
KustomizePaths: kustomizationPaths,
},
+ RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}},
}, test.labels)
- var b bytes.Buffer
- err := k.Render(context.Background(), &b, test.builds, true, "")
+ t.RequireNoError(err)
+ var b bytes.Buffer
+ err = k.Render(context.Background(), &b, test.builds, true, "")
t.CheckError(test.shouldErr, err)
t.CheckDeepEqual(test.expected, b.String())
})
@@ -643,7 +700,7 @@ func (c *kustomizeConfig) ForceDeploy() bool { return c.
func (c *kustomizeConfig) WaitForDeletions() config.WaitForDeletions { return c.waitForDeletions }
func (c *kustomizeConfig) WorkingDir() string { return c.workingDir }
func (c *kustomizeConfig) GetKubeContext() string { return testKubeContext }
-func (c *kustomizeConfig) GetKubeNamespace() string { return testNamespace }
+func (c *kustomizeConfig) GetKubeNamespace() string { return c.Opts.Namespace }
func (c *kustomizeConfig) Pipeline() latest.Pipeline {
var pipeline latest.Pipeline
pipeline.Deploy.DeployType.KustomizeDeploy = &c.kustomize
diff --git a/pkg/skaffold/deploy/resource/deployment.go b/pkg/skaffold/deploy/resource/deployment.go
index c648091e04a..7890e59dd40 100644
--- a/pkg/skaffold/deploy/resource/deployment.go
+++ b/pkg/skaffold/deploy/resource/deployment.go
@@ -101,7 +101,7 @@ func (d *Deployment) WithValidator(pd diag.Diagnose) *Deployment {
}
func (d *Deployment) CheckStatus(ctx context.Context, cfg kubectl.Config) {
- kubeCtl := kubectl.NewCLI(cfg)
+ kubeCtl := kubectl.NewCLI(cfg, "")
b, err := kubeCtl.RunOut(ctx, "rollout", "status", "deployment", d.name, "--namespace", d.namespace, "--watch=false")
if ctx.Err() != nil {
diff --git a/pkg/skaffold/kubectl/cli.go b/pkg/skaffold/kubectl/cli.go
index 8a5bd621735..8ae29360bb4 100644
--- a/pkg/skaffold/kubectl/cli.go
+++ b/pkg/skaffold/kubectl/cli.go
@@ -41,11 +41,18 @@ type Config interface {
GetKubeNamespace() string
}
-func NewCLI(cfg Config) *CLI {
+// NewCLI creates a new kubectl CLI whereby the namespace from command
+// line / environment variable takes precedence over "default namespace"
+// defined in deployer configuration
+func NewCLI(cfg Config, defaultNamespace string) *CLI {
+ ns := defaultNamespace
+ if nsFromOpts := cfg.GetKubeNamespace(); nsFromOpts != "" {
+ ns = nsFromOpts
+ }
return &CLI{
KubeContext: cfg.GetKubeContext(),
KubeConfig: cfg.GetKubeConfig(),
- Namespace: cfg.GetKubeNamespace(),
+ Namespace: ns,
}
}
diff --git a/pkg/skaffold/kubectl/cli_test.go b/pkg/skaffold/kubectl/cli_test.go
index 26096c6844a..f3377299b85 100644
--- a/pkg/skaffold/kubectl/cli_test.go
+++ b/pkg/skaffold/kubectl/cli_test.go
@@ -69,7 +69,7 @@ func TestCLI(t *testing.T) {
kubeContext: kubeContext,
kubeConfig: test.kubeconfig,
namespace: test.namespace,
- })
+ }, "")
err := cli.Run(context.Background(), nil, nil, "exec", "arg1", "arg2")
t.CheckNoError(err)
@@ -88,7 +88,7 @@ func TestCLI(t *testing.T) {
kubeContext: kubeContext,
kubeConfig: test.kubeconfig,
namespace: test.namespace,
- })
+ }, "")
out, err := cli.RunOut(context.Background(), "exec", "arg1", "arg2")
t.CheckNoError(err)
@@ -108,7 +108,7 @@ func TestCLI(t *testing.T) {
kubeContext: kubeContext,
kubeConfig: test.kubeconfig,
namespace: test.namespace,
- })
+ }, "")
cmd := cli.CommandWithStrictCancellation(context.Background(), "exec", "arg1", "arg2")
out, err := util.RunCmdOut(cmd.Cmd)
diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go
index 7307230de50..fadbbe39088 100644
--- a/pkg/skaffold/runner/deploy_test.go
+++ b/pkg/skaffold/runner/deploy_test.go
@@ -144,14 +144,16 @@ func TestSkaffoldDeployRenderOnly(t *testing.T) {
KubeContext: "does-not-exist",
}
+ deployer, err := getDeployer(runCtx, nil)
+ t.RequireNoError(err)
r := SkaffoldRunner{
runCtx: runCtx,
- kubectlCLI: kubectl.NewCLI(runCtx),
- deployer: getDeployer(runCtx, nil),
+ kubectlCLI: kubectl.NewCLI(runCtx, ""),
+ deployer: deployer,
}
var builds []build.Artifact
- err := r.Deploy(context.Background(), ioutil.Discard, builds)
+ err = r.Deploy(context.Background(), ioutil.Discard, builds)
t.CheckNoError(err)
})
diff --git a/pkg/skaffold/runner/load_images_test.go b/pkg/skaffold/runner/load_images_test.go
index fe10cac6eba..063fb67dee9 100644
--- a/pkg/skaffold/runner/load_images_test.go
+++ b/pkg/skaffold/runner/load_images_test.go
@@ -184,7 +184,7 @@ func runImageLoadingTests(t *testing.T, tests []ImageLoadingTest, loadingFunc fu
r := &SkaffoldRunner{
runCtx: runCtx,
- kubectlCLI: kubectl.NewCLI(runCtx),
+ kubectlCLI: kubectl.NewCLI(runCtx, ""),
builds: test.built,
}
err := loadingFunc(r, test)
diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go
index 0240dd70bca..905cc7879b5 100644
--- a/pkg/skaffold/runner/new.go
+++ b/pkg/skaffold/runner/new.go
@@ -43,7 +43,7 @@ import (
// NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig
func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) {
- kubectlCLI := kubectl.NewCLI(runCtx)
+ kubectlCLI := kubectl.NewCLI(runCtx, "")
tagger, err := getTagger(runCtx)
if err != nil {
@@ -63,7 +63,11 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) {
labeller := deploy.NewLabeller(runCtx.AddSkaffoldLabels(), runCtx.CustomLabels())
tester := getTester(runCtx, imagesAreLocal)
syncer := getSyncer(runCtx)
- deployer := getDeployer(runCtx, labeller.Labels())
+ var deployer deploy.Deployer
+ deployer, err = getDeployer(runCtx, labeller.Labels())
+ if err != nil {
+ return nil, fmt.Errorf("creating deployer: %w", err)
+ }
depLister := func(ctx context.Context, artifact *latest.Artifact) ([]string, error) {
buildDependencies, err := build.DependenciesForArtifact(ctx, artifact, runCtx.GetInsecureRegistries())
@@ -192,7 +196,7 @@ func getSyncer(cfg sync.Config) sync.Syncer {
return sync.NewSyncer(cfg)
}
-func getDeployer(cfg deploy.Config, labels map[string]string) deploy.Deployer {
+func getDeployer(cfg deploy.Config, labels map[string]string) (deploy.Deployer, error) {
d := cfg.Pipeline().Deploy
var deployers deploy.DeployerMux
@@ -206,19 +210,27 @@ func getDeployer(cfg deploy.Config, labels map[string]string) deploy.Deployer {
}
if d.KubectlDeploy != nil {
- deployers = append(deployers, deploy.NewKubectlDeployer(cfg, labels))
+ deployer, err := deploy.NewKubectlDeployer(cfg, labels)
+ if err != nil {
+ return nil, err
+ }
+ deployers = append(deployers, deployer)
}
if d.KustomizeDeploy != nil {
- deployers = append(deployers, deploy.NewKustomizeDeployer(cfg, labels))
+ deployer, err := deploy.NewKustomizeDeployer(cfg, labels)
+ if err != nil {
+ return nil, err
+ }
+ deployers = append(deployers, deployer)
}
// avoid muxing overhead when only a single deployer is configured
if len(deployers) == 1 {
- return deployers[0]
+ return deployers[0], nil
}
- return deployers
+ return deployers, nil
}
func getTagger(runCtx *runcontext.RunContext) (tag.Tagger, error) {
diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go
index aaeea6b2efc..b47e464dac7 100644
--- a/pkg/skaffold/runner/new_test.go
+++ b/pkg/skaffold/runner/new_test.go
@@ -27,90 +27,93 @@ import (
"github.com/GoogleContainerTools/skaffold/testutil"
)
-func TestGetDeployer(t *testing.T) {
- tests := []struct {
- description string
- cfg latest.DeployType
- expected deploy.Deployer
- }{
- {
- description: "no deployer",
- expected: deploy.DeployerMux{},
- },
- {
- description: "helm deployer",
- cfg: latest.DeployType{HelmDeploy: &latest.HelmDeploy{}},
- expected: deploy.NewHelmDeployer(&runcontext.RunContext{}, nil),
- },
- {
- description: "kubectl deployer",
- cfg: latest.DeployType{KubectlDeploy: &latest.KubectlDeploy{}},
- expected: deploy.NewKubectlDeployer(&runcontext.RunContext{
- Cfg: latest.Pipeline{
- Deploy: latest.DeployConfig{
- DeployType: latest.DeployType{
- KubectlDeploy: &latest.KubectlDeploy{
- Flags: latest.KubectlFlags{},
+func TestGetDeployer(tOuter *testing.T) {
+ testutil.Run(tOuter, "TestGetDeployer", func(t *testutil.T) {
+ tests := []struct {
+ description string
+ cfg latest.DeployType
+ expected deploy.Deployer
+ }{
+ {
+ description: "no deployer",
+ expected: deploy.DeployerMux{},
+ },
+ {
+ description: "helm deployer",
+ cfg: latest.DeployType{HelmDeploy: &latest.HelmDeploy{}},
+ expected: deploy.NewHelmDeployer(&runcontext.RunContext{}, nil),
+ },
+ {
+ description: "kubectl deployer",
+ cfg: latest.DeployType{KubectlDeploy: &latest.KubectlDeploy{}},
+ expected: t.RequireNonNilResult(deploy.NewKubectlDeployer(&runcontext.RunContext{
+ Cfg: latest.Pipeline{
+ Deploy: latest.DeployConfig{
+ DeployType: latest.DeployType{
+ KubectlDeploy: &latest.KubectlDeploy{
+ Flags: latest.KubectlFlags{},
+ },
},
},
},
- },
- }, nil),
- },
- {
- description: "kustomize deployer",
- cfg: latest.DeployType{KustomizeDeploy: &latest.KustomizeDeploy{}},
- expected: deploy.NewKustomizeDeployer(&runcontext.RunContext{
- Cfg: latest.Pipeline{
- Deploy: latest.DeployConfig{
- DeployType: latest.DeployType{
- KustomizeDeploy: &latest.KustomizeDeploy{
- Flags: latest.KubectlFlags{},
+ }, nil)).(deploy.Deployer),
+ },
+ {
+ description: "kustomize deployer",
+ cfg: latest.DeployType{KustomizeDeploy: &latest.KustomizeDeploy{}},
+ expected: t.RequireNonNilResult(deploy.NewKustomizeDeployer(&runcontext.RunContext{
+ Cfg: latest.Pipeline{
+ Deploy: latest.DeployConfig{
+ DeployType: latest.DeployType{
+ KustomizeDeploy: &latest.KustomizeDeploy{
+ Flags: latest.KubectlFlags{},
+ },
},
},
},
- },
- }, nil),
- },
- {
- description: "kpt deployer",
- cfg: latest.DeployType{KptDeploy: &latest.KptDeploy{}},
- expected: deploy.NewKptDeployer(&runcontext.RunContext{}, nil),
- },
- {
- description: "multiple deployers",
- cfg: latest.DeployType{
- HelmDeploy: &latest.HelmDeploy{},
- KptDeploy: &latest.KptDeploy{},
+ }, nil)).(deploy.Deployer),
},
- expected: deploy.DeployerMux{
- deploy.NewHelmDeployer(&runcontext.RunContext{}, nil),
- deploy.NewKptDeployer(&runcontext.RunContext{}, nil),
+ {
+ description: "kpt deployer",
+ cfg: latest.DeployType{KptDeploy: &latest.KptDeploy{}},
+ expected: deploy.NewKptDeployer(&runcontext.RunContext{}, nil),
},
- },
- }
- for _, test := range tests {
- testutil.Run(t, test.description, func(t *testutil.T) {
- deployer := getDeployer(&runcontext.RunContext{
- Cfg: latest.Pipeline{
- Deploy: latest.DeployConfig{
- DeployType: test.cfg,
- },
+ {
+ description: "multiple deployers",
+ cfg: latest.DeployType{
+ HelmDeploy: &latest.HelmDeploy{},
+ KptDeploy: &latest.KptDeploy{},
},
- }, nil)
+ expected: deploy.DeployerMux{
+ deploy.NewHelmDeployer(&runcontext.RunContext{}, nil),
+ deploy.NewKptDeployer(&runcontext.RunContext{}, nil),
+ },
+ },
+ }
+ for _, test := range tests {
+ testutil.Run(tOuter, test.description, func(t *testutil.T) {
+ deployer, err := getDeployer(&runcontext.RunContext{
+ Cfg: latest.Pipeline{
+ Deploy: latest.DeployConfig{
+ DeployType: test.cfg,
+ },
+ },
+ }, nil)
- t.CheckTypeEquality(test.expected, deployer)
+ t.RequireNoError(err)
+ t.CheckTypeEquality(test.expected, deployer)
- if reflect.TypeOf(test.expected) == reflect.TypeOf(deploy.DeployerMux{}) {
- expected := test.expected.(deploy.DeployerMux)
- deployers := deployer.(deploy.DeployerMux)
- t.CheckDeepEqual(len(expected), len(deployers))
- for i, v := range expected {
- t.CheckTypeEquality(v, deployers[i])
+ if reflect.TypeOf(test.expected) == reflect.TypeOf(deploy.DeployerMux{}) {
+ expected := test.expected.(deploy.DeployerMux)
+ deployers := deployer.(deploy.DeployerMux)
+ t.CheckDeepEqual(len(expected), len(deployers))
+ for i, v := range expected {
+ t.CheckTypeEquality(v, deployers[i])
+ }
}
- }
- })
- }
+ })
+ }
+ })
}
func TestCreateComponents(t *testing.T) {
diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go
index 32c09c7be42..3948a651399 100644
--- a/pkg/skaffold/schema/latest/config.go
+++ b/pkg/skaffold/schema/latest/config.go
@@ -474,6 +474,9 @@ type KubectlDeploy struct {
// Flags are additional flags passed to `kubectl`.
Flags KubectlFlags `yaml:"flags,omitempty"`
+
+ // DefaultNamespace is the default namespace passed to kubectl on deployment if no other override is given.
+ DefaultNamespace *string `yaml:"defaultNamespace,omitempty"`
}
// KubectlFlags are additional flags passed on the command
@@ -528,6 +531,9 @@ type KustomizeDeploy struct {
// BuildArgs are additional args passed to `kustomize build`.
BuildArgs []string `yaml:"buildArgs,omitempty"`
+
+ // DefaultNamespace is the default namespace passed to kubectl on deployment if no other override is given.
+ DefaultNamespace *string `yaml:"defaultNamespace,omitempty"`
}
// KptDeploy *alpha* uses the `kpt` CLI to manage and deploy manifests.
diff --git a/pkg/skaffold/sync/types.go b/pkg/skaffold/sync/types.go
index c0eeb65b6a0..d6904e55f11 100644
--- a/pkg/skaffold/sync/types.go
+++ b/pkg/skaffold/sync/types.go
@@ -48,7 +48,7 @@ type Config interface {
func NewSyncer(cfg Config) Syncer {
return &podSyncer{
- kubectl: pkgkubectl.NewCLI(cfg),
+ kubectl: pkgkubectl.NewCLI(cfg, ""),
namespaces: cfg.GetNamespaces(),
}
}
diff --git a/testutil/checks.go b/testutil/checks.go
index 5e12c5c4e33..cc3e7d10d27 100644
--- a/testutil/checks.go
+++ b/testutil/checks.go
@@ -48,3 +48,17 @@ func (t *T) RequireNoError(err error) {
t.FailNow()
}
}
+
+func (t *T) RequireNonNilResult(x interface{}, err error) interface{} {
+ t.Helper()
+
+ if err != nil {
+ t.Errorf("unexpected error (failing test now): %s", err)
+ t.FailNow()
+ }
+ if x == nil {
+ t.Errorf("unexpected nil value (failing test now)")
+ t.FailNow()
+ }
+ return x
+}