Repository: GoogleContainerTools/skaffold
Pull request #7723 (2022-08-02 21:11:53)

feat: fix render and deploy when using kustomize and helm deployer.
In this PR, 

Completely addressing the issue #7675 to support backward compatibility. 

Changes in this PR
- If Raw manifests are defined, even if kubectl deployer is not set add it.  e.g. 
```
manifests:
  rawYaml:
  - frontend/k8s/*.yaml
deploy:
      helm:
        releases:
        - name: backend
          chartPath: backend
```
In the above example, skaffold was configured to deploy helm charts. The frontend k8s manifests were rendered but never deployed. 

- When running `skaffold render` with legacy helm deploy stanza, force helm rendering. 
In the above same config, only kubectl manifests were rendered with `skaffold render`. This behavior is backward incompatible. 


=== TODO
1) follow up if removing duplicates `render.helm.releases` and `deploy.helm.release` makes sense. @marlon-gamez discussed before it could be confusing to remove these duplicated fields.


codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#7723](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (a0846ae) into [main](https://codecov.io/gh/GoogleContainerTools/skaffold/commit/290280e8c6c19231e9ed5b5c027f2c01d22f75be?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (290280e) will **decrease** coverage by `3.87%`.
> The diff coverage is `53.59%`.

```diff
@@            Coverage Diff             @@
##             main    #7723      +/-   ##
==========================================
- Coverage   70.48%   66.60%   -3.88%     
==========================================
  Files         515      587      +72     
  Lines       23150    28172    +5022     
==========================================
+ Hits        16317    18765    +2448     
- Misses       5776     8041    +2265     
- Partials     1057     1366     +309     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | Coverage Δ | |
|---|---|---|
| [cmd/skaffold/app/cmd/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy9leHBvcnQuZ28=) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `40.90% <0.00%> (-12.94%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/test.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdGVzdC5nbw==) | `44.44% <0.00%> (ø)` | |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL3NrYWZmb2xkLmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF90ZXN0cy5nbw==) | `62.50% <14.28%> (-1.14%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `35.48% <18.18%> (-5.90%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbHNwLmdv) | `28.12% <28.12%> (ø)` | |
| [cmd/skaffold/app/cmd/run.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcnVuLmdv) | `64.28% <33.33%> (-9.63%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `56.41% <37.50%> (-20.07%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/verify.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdmVyaWZ5Lmdv) | `41.17% <41.17%> (ø)` | |
| ... and [344 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7723/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)

aaron-prindle: Seems there is a test error atm:
```
    --- FAIL: TestRun/multiple_renderers_mixed_in#01 (3.37s)
```

also a small lint error related to a newline
tejal29: > 

fixed it. Some bad rebasing. 

Patch:
diff --git a/integration/examples/getting-started-kustomize/skaffold.yaml b/integration/examples/getting-started-kustomize/skaffold.yaml
index 03c55d803..d0bd87a40 100644
--- a/integration/examples/getting-started-kustomize/skaffold.yaml
+++ b/integration/examples/getting-started-kustomize/skaffold.yaml
@@ -12,6 +12,8 @@ manifests:
   kustomize:
     paths:
     - overlays/dev
+deploy:
+  kubectl: {}
 profiles:
 - name: prod
   manifests:
diff --git a/pkg/skaffold/build/bazel/build.go b/pkg/skaffold/build/bazel/build.go
index 5fc40c76c..29d2930fd 100644
--- a/pkg/skaffold/build/bazel/build.go
+++ b/pkg/skaffold/build/bazel/build.go
@@ -85,7 +85,7 @@ func (b *Builder) buildTar(ctx context.Context, out io.Writer, workspace string,
 		return "", fmt.Errorf("getting bazel tar path: %w", err)
 	}
 
-	return filepath.Join(workspace, tarPath), nil
+	return tarPath, nil
 }
 
 func (b *Builder) loadImage(ctx context.Context, out io.Writer, tarPath string, a *latest.BazelArtifact, tag string) (string, error) {
@@ -129,7 +129,19 @@ func bazelTarPath(ctx context.Context, workspace string, a *latest.BazelArtifact
 		return "", err
 	}
 
-	return strings.TrimSpace(string(buf)), nil
+	targetPath := strings.TrimSpace(string(buf))
+
+	cmd = exec.CommandContext(ctx, "bazel", "info", "execution_root")
+	cmd.Dir = workspace
+
+	buf, err = util.RunCmdOut(ctx, cmd)
+	if err != nil {
+		return "", err
+	}
+
+	execRoot := strings.TrimSpace(string(buf))
+
+	return filepath.Join(execRoot, targetPath), nil
 }
 
 func trimTarget(buildTarget string) string {
diff --git a/pkg/skaffold/render/renderer/renderer.go b/pkg/skaffold/render/renderer/renderer.go
index f0926f1ce..2fa973e7f 100644
--- a/pkg/skaffold/render/renderer/renderer.go
+++ b/pkg/skaffold/render/renderer/renderer.go
@@ -27,7 +27,6 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/helm"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kpt"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kubectl"
-	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/noop"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
 )
 
@@ -39,10 +38,7 @@ type Renderer interface {
 }
 
 // New creates a new Renderer object from the latestV2 API schema.
-func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig, hydrationDir string, labels map[string]string, usingLegacyHelmDeploy bool, command string, configName string) (GroupRenderer, error) {
-	if usingLegacyHelmDeploy && command != "render" {
-		return GroupRenderer{noop.New(renderCfg, cfg.GetWorkingDir(), hydrationDir, labels)}, nil
-	}
+func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig, hydrationDir string, labels map[string]string, configName string) (GroupRenderer, error) {
 	if renderCfg.Validate != nil || renderCfg.Transform != nil || renderCfg.Kpt != nil {
 		r, err := kpt.New(cfg, renderCfg, hydrationDir, labels, configName, cfg.GetNamespace())
 		if err != nil {
@@ -53,7 +49,6 @@ func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig,
 	}
 
 	var rs GroupRenderer
-
 	if renderCfg.RawK8s != nil || renderCfg.Kustomize != nil {
 		r, err := kubectl.New(cfg, renderCfg, labels, configName, cfg.GetNamespace())
 		if err != nil {
diff --git a/pkg/skaffold/runner/deployer.go b/pkg/skaffold/runner/deployer.go
index 8d25e304a..828a72aba 100644
--- a/pkg/skaffold/runner/deployer.go
+++ b/pkg/skaffold/runner/deployer.go
@@ -173,6 +173,9 @@ func GetDeployer(ctx context.Context, runCtx *runcontext.RunContext, labeller *l
 			if err != nil {
 				return nil, err
 			}
+			if len(deployers) > 0 {
+				return nil, fmt.Errorf("kpt deployer mixed in with other deployers not supported yet. Please use only kpt deployer")
+			}
 			deployers = append(deployers, deployer)
 		}
 		if d.CloudRunDeploy != nil {
@@ -199,8 +202,6 @@ The default deployer will honor a select set of deploy configuration from an exi
   - deploy.Logs.Prefix
   - deploy.Kubectl.Flags
   - deploy.Kubectl.DefaultNamespace
-  - deploy.Kustomize.Flags
-  - deploy.Kustomize.DefaultNamespace
 
 For a multi-config project, we do not currently support resolving conflicts between differing sets of this deploy configuration.
 Therefore, in this function we do implicit validation of the provided configuration, and fail if any conflict cannot be resolved.
diff --git a/pkg/skaffold/runner/renderer.go b/pkg/skaffold/runner/renderer.go
index 3ff54bccf..da9360b97 100644
--- a/pkg/skaffold/runner/renderer.go
+++ b/pkg/skaffold/runner/renderer.go
@@ -20,7 +20,9 @@ import (
 	"context"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/helm"
 	runcontext "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext/v2"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
 )
 
 // GetRenderer creates a renderer from a given RunContext and pipeline definitions.
@@ -29,11 +31,32 @@ func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDi
 
 	var renderers renderer.GroupRenderer
 	for configName, p := range ps {
-		rs, err := renderer.New(ctx, runCtx, p.Render, hydrationDir, labels, usingLegacyHelmDeploy, runCtx.Opts.Command, configName)
+		rs, err := renderer.New(ctx, runCtx, p.Render, hydrationDir, labels, configName)
 		if err != nil {
 			return nil, err
 		}
 		renderers = append(renderers, rs...)
 	}
+	// In case of legacy helm deployer configured and render command used
+	// force a helm renderer from deploy helm config
+	if usingLegacyHelmDeploy && runCtx.Opts.Command == "render" {
+		for configName, p := range ps {
+			if p.Deploy.LegacyHelmDeploy == nil {
+				continue
+			}
+			rCfg := latest.RenderConfig{
+				Generate: latest.Generate{
+					Helm: &latest.Helm{
+						Releases: p.Deploy.LegacyHelmDeploy.Releases,
+					},
+				},
+			}
+			r, err := helm.New(runCtx, rCfg, labels, configName)
+			if err != nil {
+				return nil, err
+			}
+			renderers = append(renderers, r)
+		}
+	}
 	return renderer.NewRenderMux(renderers), nil
 }
diff --git a/pkg/skaffold/schema/defaults/defaults.go b/pkg/skaffold/schema/defaults/defaults.go
index 17b9fff0d..5a3e90cc6 100644
--- a/pkg/skaffold/schema/defaults/defaults.go
+++ b/pkg/skaffold/schema/defaults/defaults.go
@@ -119,6 +119,9 @@ func SetDefaultRenderer(c *latest.SkaffoldConfig) {
 		return
 	}
 	if len(c.Render.Generate.RawK8s) > 0 {
+		if c.Deploy.KubectlDeploy == nil {
+			c.Deploy.KubectlDeploy = &latest.KubectlDeploy{}
+		}
 		return
 	}
 	if c.Render.Generate.Kustomize != nil {
@@ -130,6 +133,9 @@ func SetDefaultRenderer(c *latest.SkaffoldConfig) {
 	if c.Deploy.LegacyHelmDeploy != nil {
 		return
 	}
+	if c.Deploy.DockerDeploy != nil {
+		return
+	}
 	// Set default manifests to "k8s/*.yaml", same as v1.
 	c.Render.Generate.RawK8s = constants.DefaultKubectlManifests
 }


Test patch:
diff --git a/integration/run_test.go b/integration/run_test.go
index fd3f7e156..b623c53ff 100644
--- a/integration/run_test.go
+++ b/integration/run_test.go
@@ -159,6 +159,12 @@ var tests = []struct {
 		dir:         "examples/multiple-renderers",
 		deployments: []string{"frontend", "backend", "go-guestbook-mongodb"},
 	},
+	{
+		description: "multiple renderers mixed in",
+		dir:         "examples/multiple-renderers",
+		args:        []string{"-p", "mix-deploy"},
+		deployments: []string{"frontend", "backend", "go-guestbook-mongodb"},
+	},
 }
 
 func TestRun(t *testing.T) {
diff --git a/pkg/skaffold/build/bazel/build_test.go b/pkg/skaffold/build/bazel/build_test.go
index 4aef30059..4d83d036c 100644
--- a/pkg/skaffold/build/bazel/build_test.go
+++ b/pkg/skaffold/build/bazel/build_test.go
@@ -18,7 +18,9 @@ package bazel
 
 import (
 	"context"
+	"fmt"
 	"io"
+	"path/filepath"
 	"testing"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
@@ -33,7 +35,7 @@ func TestBuildBazel(t *testing.T) {
 		t.NewTempDir().Mkdir("bin").Chdir()
 		t.Override(&util.DefaultExecCommand, testutil.CmdRun("bazel build //:app.tar --color=no").AndRunOut(
 			"bazel cquery //:app.tar --output starlark --starlark:expr target.files.to_list()[0].path",
-			"bin/app.tar"))
+			"bin/app.tar").AndRunOut("bazel info execution_root", ""))
 		testutil.CreateFakeImageTar("bazel:app", "bin/app.tar")
 
 		artifact := &latest.Artifact{
@@ -52,11 +54,11 @@ func TestBuildBazel(t *testing.T) {
 	})
 }
 
-func TestBazelTarPathFRespectWorkspace(t *testing.T) {
+func TestBazelTarPathPrependExecutionRoot(t *testing.T) {
 	testutil.Run(t, "", func(t *testutil.T) {
 		t.Override(&util.DefaultExecCommand, testutil.CmdRun("bazel build //:app.tar --color=no").AndRunOut(
 			"bazel cquery //:app.tar --output starlark --starlark:expr target.files.to_list()[0].path",
-			"app.tar"))
+			"app.tar").AndRunOut("bazel info execution_root", ".."))
 		testutil.CreateFakeImageTar("bazel:app", "../app.tar")
 
 		artifact := &latest.Artifact{
@@ -93,11 +95,27 @@ func TestBuildBazelFailInvalidTarget(t *testing.T) {
 }
 
 func TestBazelTarPath(t *testing.T) {
-	testutil.Run(t, "", func(t *testutil.T) {
+	testutil.Run(t, "EmptyExecutionRoot", func(t *testutil.T) {
+		osSpecificPath := filepath.Join("absolute", "path", "bin")
+		t.Override(&util.DefaultExecCommand, testutil.CmdRunOut(
+			"bazel cquery //:skaffold_example.tar --output starlark --starlark:expr target.files.to_list()[0].path --arg1 --arg2",
+			fmt.Sprintf("%s\n", osSpecificPath),
+		).AndRunOut("bazel info execution_root", ""))
+
+		bazelBin, err := bazelTarPath(context.Background(), ".", &latest.BazelArtifact{
+			BuildArgs:   []string{"--arg1", "--arg2"},
+			BuildTarget: "//:skaffold_example.tar",
+		})
+
+		t.CheckNoError(err)
+		t.CheckDeepEqual(osSpecificPath, bazelBin)
+	})
+	testutil.Run(t, "AbsoluteExecutionRoot", func(t *testutil.T) {
+		osSpecificPath := filepath.Join("var", "tmp", "bazel-execution-roots", "abcdefg", "execroot", "workspace_name")
 		t.Override(&util.DefaultExecCommand, testutil.CmdRunOut(
 			"bazel cquery //:skaffold_example.tar --output starlark --starlark:expr target.files.to_list()[0].path --arg1 --arg2",
-			"/absolute/path/bin\n",
-		))
+			"bazel-bin/darwin-fastbuild-ST-confighash/path/to/bin\n",
+		).AndRunOut("bazel info execution_root", osSpecificPath))
 
 		bazelBin, err := bazelTarPath(context.Background(), ".", &latest.BazelArtifact{
 			BuildArgs:   []string{"--arg1", "--arg2"},
@@ -105,7 +123,8 @@ func TestBazelTarPath(t *testing.T) {
 		})
 
 		t.CheckNoError(err)
-		t.CheckDeepEqual("/absolute/path/bin", bazelBin)
+		expected := filepath.Join(osSpecificPath, "bazel-bin", "darwin-fastbuild-ST-confighash", "path", "to", "bin")
+		t.CheckDeepEqual(expected, bazelBin)
 	})
 }
 
diff --git a/pkg/skaffold/runner/deployer_test.go b/pkg/skaffold/runner/deployer_test.go
index 300eb4e34..c546dae4d 100644
--- a/pkg/skaffold/runner/deployer_test.go
+++ b/pkg/skaffold/runner/deployer_test.go
@@ -174,7 +174,7 @@ func TestGetDeployer(tOuter *testing.T) {
 				cfg: latest.Pipeline{
 					Deploy: latest.DeployConfig{
 						DeployType: latest.DeployType{
-							KptDeploy:        &latest.KptDeploy{},
+							KubectlDeploy:    &latest.KubectlDeploy{},
 							LegacyHelmDeploy: &latest.LegacyHelmDeploy{},
 						},
 					},
@@ -182,9 +182,22 @@ func TestGetDeployer(tOuter *testing.T) {
 				helmVersion: `version.BuildInfo{Version:"v3.7.0"}`,
 				expected: deploy.NewDeployerMux([]deploy.Deployer{
 					&helm.Deployer{},
-					&kptV2.Deployer{},
+					&kubectl.Deployer{},
 				}, false),
 			},
+			{
+				description: "multiple deployers with kpt",
+				cfg: latest.Pipeline{
+					Deploy: latest.DeployConfig{
+						DeployType: latest.DeployType{
+							KptDeploy:        &latest.KptDeploy{},
+							LegacyHelmDeploy: &latest.LegacyHelmDeploy{},
+						},
+					},
+				},
+				shouldErr:   true,
+				helmVersion: `version.BuildInfo{Version:"v3.7.0"}`,
+			},
 			{
 				description: "apply does not allow multiple deployers when a helm namespace is set",
 				apply:       true,
diff --git a/pkg/skaffold/runner/renderer_test.go b/pkg/skaffold/runner/renderer_test.go
new file mode 100644
index 000000000..00b6da1d9
--- /dev/null
+++ b/pkg/skaffold/runner/renderer_test.go
@@ -0,0 +1,139 @@
+/*
+Copyright 2021 The Skaffold Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package runner
+
+import (
+	"context"
+	"testing"
+
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/helm"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kpt"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kubectl"
+	runcontext "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext/v2"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/testutil"
+)
+
+func TestGetRenderer(tOuter *testing.T) {
+	rc := &runcontext.RunContext{
+		Pipelines: runcontext.NewPipelines(
+			map[string]latest.Pipeline{
+				"default": {},
+			})}
+	labels := map[string]string{}
+	kubectlCfg := latest.RenderConfig{
+		Generate: latest.Generate{
+			RawK8s: []string{"k8s/*"}},
+	}
+	helmConfig := latest.RenderConfig{
+		Generate: latest.Generate{
+			Helm: &latest.Helm{
+				Releases: []latest.HelmRelease{
+					{Name: "test", ChartPath: "./test"},
+					{Name: "test1", ChartPath: "./test1"},
+				},
+			},
+		},
+	}
+	kptConfig := latest.RenderConfig{
+		Generate: latest.Generate{
+			Kpt: []string{"kptfile"},
+		},
+	}
+	testutil.Run(tOuter, "TestGetRenderer", func(t *testutil.T) {
+		tests := []struct {
+			description       string
+			cfg               latest.Pipeline
+			expected          renderer.Renderer
+			apply             bool
+			shouldErr         bool
+			deepCheckRenderer bool
+		}{
+			{
+				description: "no renderer",
+				expected:    renderer.RenderMux{},
+			},
+			{
+				description: "legacy helm deployer",
+				cfg: latest.Pipeline{
+					Deploy: latest.DeployConfig{
+						DeployType: latest.DeployType{
+							LegacyHelmDeploy: &latest.LegacyHelmDeploy{
+								Releases: []latest.HelmRelease{
+									{Name: "test", ChartPath: "./test"},
+									{Name: "test1", ChartPath: "./test1"},
+								},
+							},
+						},
+					},
+				},
+				expected: renderer.NewRenderMux([]renderer.Renderer{
+					t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)}),
+			},
+			{
+				description: "helm renderer",
+				cfg: latest.Pipeline{
+					Render: helmConfig,
+				},
+				expected: renderer.NewRenderMux([]renderer.Renderer{
+					t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)}),
+			},
+			{
+				description: "kubectl renderer",
+				cfg: latest.Pipeline{
+					Render: kubectlCfg,
+				},
+				expected: renderer.NewRenderMux([]renderer.Renderer{
+					t.RequireNonNilResult(kubectl.New(rc, kubectlCfg, labels, "", "")).(renderer.Renderer)}),
+			},
+			{
+				description: "kpt renderer",
+				cfg: latest.Pipeline{
+					Render: kptConfig,
+				},
+				expected: renderer.NewRenderMux([]renderer.Renderer{
+					t.RequireNonNilResult(kpt.New(rc, kptConfig, "", labels, "", "")).(renderer.Renderer)}),
+			},
+			{
+				description: "kpt renderer when validate configured",
+				cfg: latest.Pipeline{
+					Render: latest.RenderConfig{
+						Generate: latest.Generate{RawK8s: []string{"test"}},
+						Validate: &[]latest.Validator{{Name: "kubeval"}},
+					},
+				},
+				expected: renderer.NewRenderMux([]renderer.Renderer{
+					t.RequireNonNilResult(kpt.New(rc, kptConfig, "", labels, "", "")).(renderer.Renderer)}),
+			},
+		}
+		for _, test := range tests {
+			testutil.Run(tOuter, test.description, func(t *testutil.T) {
+				rs, err := GetRenderer(context.Background(), &runcontext.RunContext{
+					Pipelines: runcontext.NewPipelines(
+						map[string]latest.Pipeline{
+							"default": test.cfg,
+						},
+					),
+				}, "", map[string]string{}, false)
+
+				t.CheckError(test.shouldErr, err)
+				t.CheckTypeEquality(test.expected, rs)
+			})
+		}
+	})
+}