Repository: GoogleContainerTools/skaffold
Pull request #7397 (2022-05-11 23:22:50)

chore: fix TestDockerDebug integration test
fixes: #7030

codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#7397](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (689eb13) 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.92%`.
> The diff coverage is `55.77%`.

```diff
@@            Coverage Diff             @@
##             main    #7397      +/-   ##
==========================================
- Coverage   70.48%   66.55%   -3.93%     
==========================================
  Files         515      565      +50     
  Lines       23150    27143    +3993     
==========================================
+ Hits        16317    18066    +1749     
- Misses       5776     7774    +1998     
- Partials     1057     1303     +246     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397?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/7397/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/7397/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `50.00% <0.00%> (-3.85%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/test.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/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/7397/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/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `36.66% <12.50%> (-4.72%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/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/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/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/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `55.93% <40.00%> (-20.54%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/verify.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/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%> (ø)` | |
| [cmd/skaffold/app/cmd/lint.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbGludC5nbw==) | `42.85% <42.85%> (ø)` | |
| ... and [289 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7397/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)


Patch:
diff --git a/cmd/skaffold/app/cmd/render.go b/cmd/skaffold/app/cmd/render.go
index 8ef296fc1..656aaa6af 100644
--- a/cmd/skaffold/app/cmd/render.go
+++ b/cmd/skaffold/app/cmd/render.go
@@ -25,6 +25,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
 )
@@ -74,9 +75,11 @@ func doRender(ctx context.Context, out io.Writer) error {
 			}
 		}
 
-		if _, errR := r.Render(ctx, out, bRes, offline, opts.RenderOutput); errR != nil {
+		manifests, errR := r.Render(ctx, out, bRes, offline, opts.RenderOutput)
+		if errR != nil {
 			return fmt.Errorf("rendering manifests: %w", errR)
 		}
+		manifest.Write(manifests.String(), opts.RenderOutput, out)
 		return nil
 	})
 }
diff --git a/deploy/docs-v2/cloudbuild-release.yaml b/deploy/docs-v2/cloudbuild-release.yaml
index fe87a30c8..ff1ac86fc 100644
--- a/deploy/docs-v2/cloudbuild-release.yaml
+++ b/deploy/docs-v2/cloudbuild-release.yaml
@@ -6,22 +6,22 @@ steps:
   args:
   - '-c'
   - |
-    docker pull gcr.io/$PROJECT_ID/docs-controller:latest || exit 0
+    docker pull gcr.io/$PROJECT_ID/docs-controller-v2:latest || exit 0
   # until https://github.com/GoogleCloudPlatform/cloud-builders/issues/253 is fixed
 
-# Build and push latest version of the docs-controller image
+# Build and push latest version of the docs-controller-v2 image
 - name: gcr.io/cloud-builders/docker
   args:
   - 'build'
   - '-t'
-  - 'gcr.io/$PROJECT_ID/docs-controller:latest'
+  - 'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
   - '--cache-from'
-  - 'gcr.io/$PROJECT_ID/docs-controller:latest'
+  - 'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
   - '--target'
   - 'runtime_deps'
   - 'deploy/webhook-v2'
 
-- name: gcr.io/$PROJECT_ID/docs-controller:latest
+- name: gcr.io/$PROJECT_ID/docs-controller-v2:latest
   env:
     - 'HUGO_ENV=production'
   args:
@@ -31,5 +31,5 @@ steps:
     deploy/docs-v2/build.sh https://skaffold-v2.firebaseapp.com && cd docs-v2 && firebase deploy --only hosting:release --project $PROJECT_ID
 
 images: [
-  'gcr.io/$PROJECT_ID/docs-controller:latest'
+  'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
 ]
diff --git a/deploy/docs-v2/cloudbuild.yaml b/deploy/docs-v2/cloudbuild.yaml
index ac455390c..898f11456 100644
--- a/deploy/docs-v2/cloudbuild.yaml
+++ b/deploy/docs-v2/cloudbuild.yaml
@@ -1,27 +1,27 @@
 steps:
 
-# Download the latest version of the docs-controller image
+# Download the latest version of the docs-controller-v2 image
 - name: gcr.io/cloud-builders/docker
   entrypoint: 'bash'
   args:
   - '-c'
   - |
-    docker pull gcr.io/$PROJECT_ID/docs-controller:latest || exit 0
+    docker pull gcr.io/$PROJECT_ID/docs-controller-v2:latest || exit 0
   # until https://github.com/GoogleCloudPlatform/cloud-builders/issues/253 is fixed
 
-# Build and push latest version of the docs-controller image
+# Build and push latest version of the docs-controller-v2 image
 - name: gcr.io/cloud-builders/docker
   args:
   - 'build'
   - '-t'
-  - 'gcr.io/$PROJECT_ID/docs-controller:latest'
+  - 'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
   - '--cache-from'
-  - 'gcr.io/$PROJECT_ID/docs-controller:latest'
+  - 'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
   - '--target'
   - 'runtime_deps'
   - 'deploy/webhook-v2'
 
-- name: gcr.io/$PROJECT_ID/docs-controller:latest
+- name: gcr.io/$PROJECT_ID/docs-controller-v2:latest
   env:
     - 'HUGO_ENV=production'
   args:
@@ -31,4 +31,4 @@ steps:
     deploy/docs-v2/build.sh https://skaffold-v2-latest.firebaseapp.com && cd docs-v2 && firebase deploy --only hosting:head --project $PROJECT_ID
 
 images:
-- 'gcr.io/$PROJECT_ID/docs-controller:latest'
+- 'gcr.io/$PROJECT_ID/docs-controller-v2:latest'
diff --git a/deploy/webhook-v2/Dockerfile b/deploy/webhook-v2/Dockerfile
index f7aed4ed2..b7ab72550 100644
--- a/deploy/webhook-v2/Dockerfile
+++ b/deploy/webhook-v2/Dockerfile
@@ -49,7 +49,8 @@ COPY --from=download-kubectl /kubectl /usr/local/bin/
 FROM golang:1.17 as webhook
 WORKDIR /skaffold
 COPY . .
-RUN go build -o /webhook webhook/webhook.go
+# TODO(aaron-prindle) pass ldflags for -v2
+RUN go build -o /webhook -ldflags="-X 'github.com/GoogleContainerTools/skaffold/pkg/webhook/constants.DocsVersion=-v2'" webhook/webhook.go
 
 FROM runtime_deps
 COPY --from=webhook /webhook /webhook
diff --git a/deploy/webhook-v2/cloudbuild.yaml b/deploy/webhook-v2/cloudbuild.yaml
index f97124ad0..76965bb90 100644
--- a/deploy/webhook-v2/cloudbuild.yaml
+++ b/deploy/webhook-v2/cloudbuild.yaml
@@ -1,11 +1,11 @@
 steps:
-# Download the latest version of the docs-controller image
+# Download the latest version of the docs-controller-v2 image
 - name: gcr.io/cloud-builders/docker
   entrypoint: 'bash'
   args:
     - '-c'
     - |
-      docker pull gcr.io/$PROJECT_ID/docs-controller:latest || exit 0
+      docker pull gcr.io/$PROJECT_ID/docs-controller-v2:latest || exit 0
   # until https://github.com/GoogleCloudPlatform/cloud-builders/issues/253 is fixed
 
   # Get cluster credentials
diff --git a/deploy/webhook-v2/deployment.yaml b/deploy/webhook-v2/deployment.yaml
index b8971d893..4cf11b3c2 100644
--- a/deploy/webhook-v2/deployment.yaml
+++ b/deploy/webhook-v2/deployment.yaml
@@ -20,7 +20,7 @@ subjects:
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: docs-controller
+  name: docs-controller-v2
   labels:
     run: docs
 spec:
@@ -34,8 +34,8 @@ spec:
     spec:
       serviceAccountName: admin-serviceaccount
       containers:
-      - name: docs-controller
-        image: gcr.io/k8s-skaffold/docs-controller:latest
+      - name: docs-controller-v2
+        image: gcr.io/k8s-skaffold/docs-controller-v2:latest
         args: ["/webhook"]
         volumeMounts:
         - name: webhook
diff --git a/deploy/webhook-v2/skaffold.yaml b/deploy/webhook-v2/skaffold.yaml
index 74668567b..286970af0 100644
--- a/deploy/webhook-v2/skaffold.yaml
+++ b/deploy/webhook-v2/skaffold.yaml
@@ -2,11 +2,11 @@ apiVersion: skaffold/v1alpha5
 kind: Config
 build:
     artifacts:
-    - image: gcr.io/k8s-skaffold/docs-controller
+    - image: gcr.io/k8s-skaffold/docs-controller-v2
       docker:
         dockerfile: deploy/webhook-v2/Dockerfile
         cacheFrom:
-          - gcr.io/k8s-skaffold/docs-controller
+          - gcr.io/k8s-skaffold/docs-controller-v2
 deploy:
   kubectl:
     manifests:
diff --git a/integration/skaffold/helper.go b/integration/skaffold/helper.go
index cd136e106..9415f9509 100644
--- a/integration/skaffold/helper.go
+++ b/integration/skaffold/helper.go
@@ -346,6 +346,7 @@ func (b *RunBuilder) cmd(ctx context.Context) *exec.Cmd {
 	if value, found := os.LookupEnv("SKAFFOLD_BINARY"); found {
 		skaffoldBinary = value
 	}
+	b.env = append(b.env, "SKAFFOLD_INT_TEST=true")
 	cmd := exec.CommandContext(ctx, skaffoldBinary, args...)
 	cmd.Env = append(removeSkaffoldEnvVariables(util.OSEnviron()), b.env...)
 	if b.stdin != nil {
diff --git a/pkg/skaffold/build/bazel/build.go b/pkg/skaffold/build/bazel/build.go
index 64a642037..4e3b57130 100644
--- a/pkg/skaffold/build/bazel/build.go
+++ b/pkg/skaffold/build/bazel/build.go
@@ -48,7 +48,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, artifact *latest.Art
 	}
 
 	if b.pushImages {
-		return docker.Push(tarPath, tag, b.cfg)
+		return docker.Push(tarPath, tag, b.cfg, nil)
 	}
 	return b.loadImage(ctx, out, tarPath, a, tag)
 }
diff --git a/pkg/skaffold/build/cache/details.go b/pkg/skaffold/build/cache/details.go
index d2c54085d..9cc805ee4 100644
--- a/pkg/skaffold/build/cache/details.go
+++ b/pkg/skaffold/build/cache/details.go
@@ -19,8 +19,12 @@ package cache
 import (
 	"context"
 	"io"
+	"path/filepath"
+	"strings"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 )
 
 type cacheDetails interface {
@@ -55,7 +59,7 @@ func (d found) Hash() string {
 }
 
 type needsTagging interface {
-	Tag(context.Context, *cache) error
+	Tag(context.Context, *cache, platform.Resolver) error
 }
 
 // Found locally with wrong tag. Needs retagging
@@ -69,7 +73,7 @@ func (d needsLocalTagging) Hash() string {
 	return d.hash
 }
 
-func (d needsLocalTagging) Tag(ctx context.Context, c *cache) error {
+func (d needsLocalTagging) Tag(ctx context.Context, c *cache, platforms platform.Resolver) error {
 	return c.client.Tag(ctx, d.imageID, d.tag)
 }
 
@@ -84,9 +88,10 @@ func (d needsRemoteTagging) Hash() string {
 	return d.hash
 }
 
-func (d needsRemoteTagging) Tag(ctx context.Context, c *cache) error {
+func (d needsRemoteTagging) Tag(ctx context.Context, c *cache, platforms platform.Resolver) error {
 	fqn := d.tag + "@" + d.digest // Tag is not important. We just need the registry and the digest to locate the image.
-	return docker.AddRemoteTag(fqn, d.tag, c.cfg)
+	matched := platforms.GetPlatforms(strings.Split(filepath.Base(d.tag), ":")[0])
+	return docker.AddRemoteTag(fqn, d.tag, c.cfg, util.ConvertToV1Platform(matched.Platforms[0]))
 }
 
 // Found locally. Needs pushing
diff --git a/pkg/skaffold/build/cache/lookup.go b/pkg/skaffold/build/cache/lookup.go
index 1cd308f27..86b39acb7 100644
--- a/pkg/skaffold/build/cache/lookup.go
+++ b/pkg/skaffold/build/cache/lookup.go
@@ -108,7 +108,7 @@ func (c *cache) lookupLocal(ctx context.Context, hash, tag string, entry ImageDe
 }
 
 func (c *cache) lookupRemote(ctx context.Context, hash, tag string, entry ImageDetails) cacheDetails {
-	if remoteDigest, err := docker.RemoteDigest(tag, c.cfg); err == nil {
+	if remoteDigest, err := docker.RemoteDigest(tag, c.cfg, nil); err == nil {
 		// Image exists remotely with the same tag and digest
 		if remoteDigest == entry.Digest {
 			return found{hash: hash}
@@ -117,7 +117,7 @@ func (c *cache) lookupRemote(ctx context.Context, hash, tag string, entry ImageD
 
 	// Image exists remotely with a different tag
 	fqn := tag + "@" + entry.Digest // Actual tag will be ignored but we need the registry and the digest part of it.
-	if remoteDigest, err := docker.RemoteDigest(fqn, c.cfg); err == nil {
+	if remoteDigest, err := docker.RemoteDigest(fqn, c.cfg, nil); err == nil {
 		if remoteDigest == entry.Digest {
 			return needsRemoteTagging{hash: hash, tag: tag, digest: entry.Digest}
 		}
@@ -159,7 +159,7 @@ func (c *cache) tryImport(ctx context.Context, a *latest.Artifact, tag string, h
 		entry.ID = imageID
 	}
 
-	if digest, err := docker.RemoteDigest(tag, c.cfg); err == nil {
+	if digest, err := docker.RemoteDigest(tag, c.cfg, nil); err == nil {
 		log.Entry(ctx).Debugf("Added digest for %s to cache entry", tag)
 		entry.Digest = digest
 	}
diff --git a/pkg/skaffold/build/cache/retrieve.go b/pkg/skaffold/build/cache/retrieve.go
index 7671c615e..7d5f72418 100644
--- a/pkg/skaffold/build/cache/retrieve.go
+++ b/pkg/skaffold/build/cache/retrieve.go
@@ -82,7 +82,7 @@ func (c *cache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, ar
 		case needsTagging:
 			eventV2.CacheCheckHit(artifact.ImageName)
 			output.Green.Fprintln(out, "Found. Tagging")
-			if err := result.Tag(ctx, c); err != nil {
+			if err := result.Tag(ctx, c, platforms); err != nil {
 				endTrace(instrumentation.TraceEndError(err))
 				return nil, fmt.Errorf("tagging image: %w", err)
 			}
diff --git a/pkg/skaffold/build/cluster/kaniko.go b/pkg/skaffold/build/cluster/kaniko.go
index 79c8c5014..73c1a3b63 100644
--- a/pkg/skaffold/build/cluster/kaniko.go
+++ b/pkg/skaffold/build/cluster/kaniko.go
@@ -97,7 +97,7 @@ func (b *Builder) buildWithKaniko(ctx context.Context, out io.Writer, workspace
 
 	waitForLogs()
 
-	return docker.RemoteDigest(tag, b.cfg)
+	return docker.RemoteDigest(tag, b.cfg, nil)
 }
 
 // first copy over the buildcontext tarball into the init container tmp dir via kubectl cp
diff --git a/pkg/skaffold/build/custom/build.go b/pkg/skaffold/build/custom/build.go
index da892605f..477738ddf 100644
--- a/pkg/skaffold/build/custom/build.go
+++ b/pkg/skaffold/build/custom/build.go
@@ -39,7 +39,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, artifact *latest.Art
 	}
 
 	if b.pushImages {
-		return docker.RemoteDigest(tag, b.cfg)
+		return docker.RemoteDigest(tag, b.cfg, nil)
 	}
 
 	imageID, err := b.localDocker.ImageID(ctx, tag)
diff --git a/pkg/skaffold/build/gcb/cloud_build.go b/pkg/skaffold/build/gcb/cloud_build.go
index a9418e878..8c9197e86 100644
--- a/pkg/skaffold/build/gcb/cloud_build.go
+++ b/pkg/skaffold/build/gcb/cloud_build.go
@@ -229,7 +229,7 @@ watch:
 		switch cb.Status {
 		case StatusQueued, StatusWorking, StatusUnknown:
 		case StatusSuccess:
-			digest, err = b.getDigest(cb, tag)
+			digest, err = b.getDigest(cb, tag, platform)
 			if err != nil {
 				return "", sErrors.NewErrorWithStatusCode(&proto.ActionableErr{
 					ErrCode: proto.StatusCode_BUILD_GCB_GET_BUILT_IMAGE_ERR,
@@ -290,7 +290,7 @@ func getBuildID(op *cloudbuild.Operation) (string, error) {
 	return buildMeta.Build.Id, nil
 }
 
-func (b *Builder) getDigest(cb *cloudbuild.Build, defaultToTag string) (string, error) {
+func (b *Builder) getDigest(cb *cloudbuild.Build, defaultToTag string, platforms platform.Matcher) (string, error) {
 	if cb.Results != nil && len(cb.Results.Images) == 1 {
 		return cb.Results.Images[0].Digest, nil
 	}
@@ -298,7 +298,7 @@ func (b *Builder) getDigest(cb *cloudbuild.Build, defaultToTag string) (string,
 	// The build steps pushed the image directly like when we use Jib.
 	// Retrieve the digest for that tag.
 	// TODO(dgageot): I don't think GCB can push to an insecure registry.
-	return docker.RemoteDigest(defaultToTag, b.cfg)
+	return docker.RemoteDigest(defaultToTag, b.cfg, util.ConvertToV1Platform(platforms.Platforms[0]))
 }
 
 func (b *Builder) getLogs(ctx context.Context, c *cstorage.Client, offset int64, bucket, objectName string) (io.ReadCloser, error) {
diff --git a/pkg/skaffold/build/jib/gradle.go b/pkg/skaffold/build/jib/gradle.go
index 65bac302b..a58c23b62 100644
--- a/pkg/skaffold/build/jib/gradle.go
+++ b/pkg/skaffold/build/jib/gradle.go
@@ -59,7 +59,7 @@ func (b *Builder) buildJibGradleToRegistry(ctx context.Context, out io.Writer, w
 		return "", jibToolErr(err)
 	}
 
-	return docker.RemoteDigest(tag, b.cfg)
+	return docker.RemoteDigest(tag, b.cfg, nil)
 }
 
 func (b *Builder) runGradleCommand(ctx context.Context, out io.Writer, workspace string, args []string) error {
diff --git a/pkg/skaffold/build/jib/maven.go b/pkg/skaffold/build/jib/maven.go
index c33329e9a..fe8d0a5e2 100644
--- a/pkg/skaffold/build/jib/maven.go
+++ b/pkg/skaffold/build/jib/maven.go
@@ -59,7 +59,7 @@ func (b *Builder) buildJibMavenToRegistry(ctx context.Context, out io.Writer, wo
 		return "", jibToolErr(err)
 	}
 
-	return docker.RemoteDigest(tag, b.cfg)
+	return docker.RemoteDigest(tag, b.cfg, nil)
 }
 
 func (b *Builder) runMavenCommand(ctx context.Context, out io.Writer, workspace string, args []string) error {
diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go
index c238dea87..f3a0ed197 100644
--- a/pkg/skaffold/config/options.go
+++ b/pkg/skaffold/config/options.go
@@ -17,6 +17,7 @@ limitations under the License.
 package config
 
 import (
+	"os"
 	"strings"
 	"time"
 
@@ -113,6 +114,7 @@ var RunModes = struct {
 	Render   RunMode
 	Delete   RunMode
 	Diagnose RunMode
+	IntTest  RunMode
 }{
 	Build:    "build",
 	Dev:      "dev",
@@ -122,6 +124,7 @@ var RunModes = struct {
 	Render:   "render",
 	Delete:   "delete",
 	Diagnose: "diagnose",
+	IntTest:  "testing",
 }
 
 // Prune returns true iff the user did NOT specify the --no-prune flag,
@@ -131,6 +134,9 @@ func (opts *SkaffoldOptions) Prune() bool {
 }
 
 func (opts *SkaffoldOptions) Mode() RunMode {
+	if _, ok := os.LookupEnv("SKAFFOLD_INT_TEST"); ok {
+		return RunModes.IntTest
+	}
 	return RunMode(opts.Command)
 }
 
diff --git a/pkg/skaffold/deploy/deploy_mux.go b/pkg/skaffold/deploy/deploy_mux.go
index 3b15ff02f..f94cc71dc 100644
--- a/pkg/skaffold/deploy/deploy_mux.go
+++ b/pkg/skaffold/deploy/deploy_mux.go
@@ -170,23 +170,5 @@ func (m DeployerMux) Cleanup(ctx context.Context, w io.Writer, dryRun bool, list
 	return nil
 }
 
-// TODO move this to RenderMux https://github.com/GoogleContainerTools/skaffold/issues/7330
-// func (m DeployerMux) Render(ctx context.Context, w io.Writer, as []graph.Artifact, offline bool, filepath string) error {
-//	resources, buf := []string{}, &bytes.Buffer{}
-//	for _, deployer := range m.deployers {
-//		ctx, endTrace := instrumentation.StartTrace(ctx, "Render")
-//		buf.Reset()
-//		if err := deployer.Render(ctx, buf, as, offline, "" /* never write to files */); err != nil {
-//			endTrace(instrumentation.TraceEndError(err))
-//			return err
-//		}
-//		resources = append(resources, buf.String())
-//		endTrace()
-//	}
-//
-//	allResources := strings.Join(resources, "\n---\n")
-//	return manifest.Write(strings.TrimSpace(allResources), filepath, w)
-//}
-
 // TrackBuildArtifacts should *only* be called on individual deployers. This is a noop.
 func (m DeployerMux) TrackBuildArtifacts(_ []graph.Artifact) {}
diff --git a/pkg/skaffold/deploy/kpt/kpt.go b/pkg/skaffold/deploy/kpt/kpt.go
index 65607dcf2..2cbc7b18e 100644
--- a/pkg/skaffold/deploy/kpt/kpt.go
+++ b/pkg/skaffold/deploy/kpt/kpt.go
@@ -315,6 +315,15 @@ func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []graph.Art
 	}
 	endTrace()
 
+	// Add debug transformations
+	debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig)
+	if err != nil {
+		return err
+	}
+	if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil {
+		return err
+	}
+
 	_, endTrace = instrumentation.StartTrace(ctx, "Deploy_CollectNamespaces")
 	namespaces, err := manifests.CollectNamespaces()
 	if err != nil {
diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go
index efaf16e4b..7ddc73902 100644
--- a/pkg/skaffold/deploy/kubectl/cli.go
+++ b/pkg/skaffold/deploy/kubectl/cli.go
@@ -61,7 +61,6 @@ type Config interface {
 	Tail() bool
 	PipelineForImage(imageName string) (latest.Pipeline, bool)
 	JSONParseConfig() latest.JSONParseConfig
-	GetRenderConfig() *latest.RenderConfig
 }
 
 func NewCLI(cfg Config, flags latest.KubectlFlags, defaultNamespace string) CLI {
diff --git a/pkg/skaffold/deploy/kubectl/kubectl.go b/pkg/skaffold/deploy/kubectl/kubectl.go
index fe57f94d5..1f148f3dc 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl.go
@@ -221,6 +221,15 @@ func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []graph.Art
 		return fmt.Errorf("nothing to deploy")
 	}
 
+	// Add debug transformations
+	debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig)
+	if err != nil {
+		return err
+	}
+	if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil {
+		return err
+	}
+
 	_, endTrace = instrumentation.StartTrace(ctx, "Deploy_LoadImages")
 	if err := k.imageLoader.LoadImages(childCtx, out, k.localImages, k.originalImages, builds); err != nil {
 		endTrace(instrumentation.TraceEndError(err))
diff --git a/pkg/skaffold/deploy/kustomize/kustomize.go b/pkg/skaffold/deploy/kustomize/kustomize.go
index 5b17f6fec..71d22434e 100644
--- a/pkg/skaffold/deploy/kustomize/kustomize.go
+++ b/pkg/skaffold/deploy/kustomize/kustomize.go
@@ -276,6 +276,14 @@ func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []graph.Art
 		endTrace()
 		return nil
 	}
+	// Add debug transformations
+	debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig)
+	if err != nil {
+		return err
+	}
+	if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil {
+		return err
+	}
 	endTrace()
 
 	childCtx, endTrace = instrumentation.StartTrace(ctx, "Deploy_LoadImages")
diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go
index ab5cb28aa..24b23d76e 100644
--- a/pkg/skaffold/docker/image.go
+++ b/pkg/skaffold/docker/image.go
@@ -287,7 +287,7 @@ func (l *localDaemon) ConfigFile(ctx context.Context, image string) (*v1.ConfigF
 			return nil, err
 		}
 	} else {
-		cfg, err = RetrieveRemoteConfig(image, l.cfg)
+		cfg, err = RetrieveRemoteConfig(image, l.cfg, nil)
 		if err != nil {
 			return nil, err
 		}
@@ -434,7 +434,7 @@ func (l *localDaemon) Push(ctx context.Context, out io.Writer, ref string) (stri
 	if digest == "" {
 		// Maybe this version of Docker doesn't return the digest of the image
 		// that has been pushed.
-		digest, err = RemoteDigest(ref, l.cfg)
+		digest, err = RemoteDigest(ref, l.cfg, nil)
 		if err != nil {
 			return "", fmt.Errorf("getting digest: %w", err)
 		}
diff --git a/pkg/skaffold/docker/image_util.go b/pkg/skaffold/docker/image_util.go
index 8e9a4111b..29e3c14ed 100644
--- a/pkg/skaffold/docker/image_util.go
+++ b/pkg/skaffold/docker/image_util.go
@@ -40,7 +40,7 @@ func RetrieveConfigFile(ctx context.Context, tagged string, cfg Config) (*v1.Con
 	}
 	if err != nil {
 		// No local Docker is available
-		cf, err = RetrieveRemoteConfig(tagged, cfg)
+		cf, err = RetrieveRemoteConfig(tagged, cfg, nil)
 	}
 	if err != nil {
 		return nil, fmt.Errorf("retrieving image config: %w", err)
diff --git a/pkg/skaffold/docker/remote.go b/pkg/skaffold/docker/remote.go
index 3ac4bcf6a..afa53dd5f 100644
--- a/pkg/skaffold/docker/remote.go
+++ b/pkg/skaffold/docker/remote.go
@@ -36,9 +36,9 @@ var (
 	remoteIndex  = remote.Index
 )
 
-func AddRemoteTag(src, target string, cfg Config) error {
+func AddRemoteTag(src, target string, cfg Config, platform *v1.Platform) error {
 	log.Entry(context.TODO()).Debugf("attempting to add tag %s to src %s", target, src)
-	img, err := getRemoteImage(src, cfg)
+	img, err := getRemoteImage(src, cfg, platform)
 	if err != nil {
 		return fmt.Errorf("getting image: %w", err)
 	}
@@ -51,13 +51,13 @@ func AddRemoteTag(src, target string, cfg Config) error {
 	return remote.Write(targetRef, img, remote.WithAuthFromKeychain(primaryKeychain))
 }
 
-func getRemoteDigest(identifier string, cfg Config) (string, error) {
+func getRemoteDigest(identifier string, cfg Config, platform *v1.Platform) (string, error) {
 	idx, err := getRemoteIndex(identifier, cfg)
 	if err == nil {
 		return digest(idx)
 	}
 
-	img, err := getRemoteImage(identifier, cfg)
+	img, err := getRemoteImage(identifier, cfg, platform)
 	if err != nil {
 		return "", fmt.Errorf("getting image: %w", err)
 	}
@@ -66,8 +66,8 @@ func getRemoteDigest(identifier string, cfg Config) (string, error) {
 }
 
 // RetrieveRemoteConfig retrieves the remote config file for an image
-func RetrieveRemoteConfig(identifier string, cfg Config) (*v1.ConfigFile, error) {
-	img, err := getRemoteImage(identifier, cfg)
+func RetrieveRemoteConfig(identifier string, cfg Config, platform *v1.Platform) (*v1.ConfigFile, error) {
+	img, err := getRemoteImage(identifier, cfg, platform)
 	if err != nil {
 		return nil, err
 	}
@@ -76,7 +76,7 @@ func RetrieveRemoteConfig(identifier string, cfg Config) (*v1.ConfigFile, error)
 }
 
 // Push pushes the tarball image
-func Push(tarPath, tag string, cfg Config) (string, error) {
+func Push(tarPath, tag string, cfg Config, platform *v1.Platform) (string, error) {
 	t, err := name.NewTag(tag, name.WeakValidation)
 	if err != nil {
 		return "", fmt.Errorf("parsing tag %q: %w", tag, err)
@@ -91,16 +91,22 @@ func Push(tarPath, tag string, cfg Config) (string, error) {
 		return "", fmt.Errorf("%s %q: %w", sErrors.PushImageErr, t, err)
 	}
 
-	return getRemoteDigest(tag, cfg)
+	return getRemoteDigest(tag, cfg, platform)
 }
 
-func getRemoteImage(identifier string, cfg Config) (v1.Image, error) {
+func getRemoteImage(identifier string, cfg Config, platform *v1.Platform) (v1.Image, error) {
 	ref, err := parseReference(identifier, cfg)
 	if err != nil {
 		return nil, err
 	}
+	options := []remote.Option{
+		remote.WithAuthFromKeychain(primaryKeychain),
+	}
+	if platform != nil {
+		options = append(options, remote.WithPlatform(*platform))
+	}
 
-	return remoteImage(ref, remote.WithAuthFromKeychain(primaryKeychain))
+	return remoteImage(ref, options...)
 }
 
 func getRemoteIndex(identifier string, cfg Config) (v1.ImageIndex, error) {
diff --git a/pkg/skaffold/platform/resolver.go b/pkg/skaffold/platform/resolver.go
index b5ea0fa53..49c476f7f 100644
--- a/pkg/skaffold/platform/resolver.go
+++ b/pkg/skaffold/platform/resolver.go
@@ -84,7 +84,7 @@ func NewResolver(ctx context.Context, pipelines []latest.Pipeline, cliPlatformsS
 			} else if p := platforms.Intersect(fromClusterNodes); p.IsNotEmpty() {
 				platforms = p
 			} else {
-				return r, fmt.Errorf("build target platforms %q do not match active kubernetes cluster node platforms %q", platforms, fromClusterNodes)
+				log.Entry(ctx).Warnf("build target platforms %q do not match active kubernetes cluster node platforms %q", platforms, fromClusterNodes)
 			}
 		}
 		instrumentation.AddResolvedBuildTargetPlatforms(platforms.String())
diff --git a/pkg/skaffold/render/config.go b/pkg/skaffold/render/config.go
index d992cf9ab..53ee05e63 100644
--- a/pkg/skaffold/render/config.go
+++ b/pkg/skaffold/render/config.go
@@ -21,7 +21,6 @@ import (
 )
 
 type Config interface {
-	GetRenderConfig() *latest.RenderConfig
 	GetWorkingDir() string
 	TransformAllowList() []latest.ResourceFilter
 	TransformDenyList() []latest.ResourceFilter
diff --git a/pkg/skaffold/render/renderer/kpt/kpt.go b/pkg/skaffold/render/renderer/kpt/kpt.go
index 82b7ea52a..e8ff2f3ca 100644
--- a/pkg/skaffold/render/renderer/kpt/kpt.go
+++ b/pkg/skaffold/render/renderer/kpt/kpt.go
@@ -59,17 +59,16 @@ const (
 	DryFileName = "manifests.yaml"
 )
 
-func New(cfg render.Config, hydrationDir string,
-	labels map[string]string) (*Kpt, error) {
-	generator := generate.NewGenerator(cfg.GetWorkingDir(), cfg.GetRenderConfig().Generate)
+func New(cfg render.Config, rCfg latest.RenderConfig, hydrationDir string, labels map[string]string) (*Kpt, error) {
+	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate)
 	transformAllowlist, transformDenylist, err := rUtil.ConsolidateTransformConfiguration(cfg)
 	if err != nil {
 		return nil, err
 	}
 	var validator validate.Validator
-	if cfg.GetRenderConfig().Validate != nil {
+	if rCfg.Validate != nil {
 		var err error
-		validator, err = validate.NewValidator(*cfg.GetRenderConfig().Validate)
+		validator, err = validate.NewValidator(*rCfg.Validate)
 		if err != nil {
 			return nil, err
 		}
@@ -78,9 +77,9 @@ func New(cfg render.Config, hydrationDir string,
 	}
 
 	var transformer transform.Transformer
-	if cfg.GetRenderConfig().Transform != nil {
+	if rCfg.Transform != nil {
 		var err error
-		transformer, err = transform.NewTransformer(*cfg.GetRenderConfig().Transform)
+		transformer, err = transform.NewTransformer(*rCfg.Transform)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/skaffold/render/renderer/kubectl/kubectl.go b/pkg/skaffold/render/renderer/kubectl/kubectl.go
index c867655a4..7ce5637cb 100644
--- a/pkg/skaffold/render/renderer/kubectl/kubectl.go
+++ b/pkg/skaffold/render/renderer/kubectl/kubectl.go
@@ -40,8 +40,8 @@ type Kubectl struct {
 	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
 }
 
-func New(cfg render.Config, labels map[string]string) (Kubectl, error) {
-	generator := generate.NewGenerator(cfg.GetWorkingDir(), cfg.GetRenderConfig().Generate)
+func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string) (Kubectl, error) {
+	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate)
 	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
 	if err != nil {
 		return Kubectl{}, err
diff --git a/pkg/skaffold/render/renderer/noop/noop.go b/pkg/skaffold/render/renderer/noop/noop.go
index cdf575f60..ac975e434 100644
--- a/pkg/skaffold/render/renderer/noop/noop.go
+++ b/pkg/skaffold/render/renderer/noop/noop.go
@@ -30,7 +30,7 @@ import (
 // TODO(marlongamez): add Helm renderer which uses helm template and can feed into deployers
 type Noop struct{}
 
-func New(_ *latest.RenderConfig, _, _ string, _ map[string]string) (Noop, error) {
+func New(_ latest.RenderConfig, _, _ string, _ map[string]string) (Noop, error) {
 	return Noop{}, nil
 }
 
diff --git a/pkg/skaffold/render/renderer/render_mux.go b/pkg/skaffold/render/renderer/render_mux.go
new file mode 100644
index 000000000..05f45826e
--- /dev/null
+++ b/pkg/skaffold/render/renderer/render_mux.go
@@ -0,0 +1,73 @@
+/*
+Copyright 2022 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 renderer
+
+import (
+	"context"
+	"io"
+	"strconv"
+
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
+	eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringset"
+)
+
+// RenderMux forwards all method calls to the renderers it contains.
+// When encountering an error, it aborts and returns the error. Otherwise,
+// it collects the results and returns all the manifests.
+type RenderMux struct {
+	renderers []Renderer
+}
+
+func NewRenderMux(renderers []Renderer) Renderer {
+	return RenderMux{renderers: renderers}
+}
+
+func (r RenderMux) Render(ctx context.Context, out io.Writer, artifacts []graph.Artifact, offline bool, outputPath string) (manifest.ManifestList, error) {
+	allManifests := manifest.ManifestList{}
+	for i, renderer := range r.renderers {
+		eventV2.RendererInProgress(i)
+		w, ctx := output.WithEventContext(ctx, out, constants.Render, strconv.Itoa(i))
+		ctx, endTrace := instrumentation.StartTrace(ctx, "Render")
+		ms, err := renderer.Render(ctx, w, artifacts, offline, "" /* never write to files */)
+		if err != nil {
+			eventV2.RendererFailed(i, err)
+			endTrace(instrumentation.TraceEndError(err))
+			return nil, err
+		}
+		allManifests = append(allManifests, ms...)
+		eventV2.RendererSucceeded(i)
+		endTrace()
+	}
+	return allManifests, nil
+}
+
+func (r RenderMux) ManifestDeps() ([]string, error) {
+	deps := stringset.New()
+	for _, renderer := range r.renderers {
+		result, err := renderer.ManifestDeps()
+		if err != nil {
+			return nil, err
+		}
+		deps.Insert(result...)
+	}
+	return deps.ToList(), nil
+}
diff --git a/pkg/skaffold/render/renderer/renderer.go b/pkg/skaffold/render/renderer/renderer.go
index ca4533fea..c59ea4a97 100644
--- a/pkg/skaffold/render/renderer/renderer.go
+++ b/pkg/skaffold/render/renderer/renderer.go
@@ -27,6 +27,7 @@ import (
 	"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"
 )
 
 type Renderer interface {
@@ -37,15 +38,14 @@ type Renderer interface {
 }
 
 // New creates a new Renderer object from the latestV2 API schema.
-func New(cfg render.Config, hydrationDir string, labels map[string]string, usingLegacyHelmDeploy bool) (Renderer, error) {
-	renderCfg := cfg.GetRenderConfig()
+func New(cfg render.Config, renderCfg latest.RenderConfig, hydrationDir string, labels map[string]string, usingLegacyHelmDeploy bool) (Renderer, error) {
 	if usingLegacyHelmDeploy {
 		return noop.New(renderCfg, cfg.GetWorkingDir(), hydrationDir, labels)
 	}
 	if renderCfg.Validate == nil && renderCfg.Transform == nil && renderCfg.Kpt == nil {
 		log.Entry(context.TODO()).Debug("setting up kubectl renderer")
-		return kubectl.New(cfg, labels)
+		return kubectl.New(cfg, renderCfg, labels)
 	}
 	log.Entry(context.TODO()).Infof("setting up kpt renderer")
-	return kpt.New(cfg, hydrationDir, labels)
+	return kpt.New(cfg, renderCfg, hydrationDir, labels)
 }
diff --git a/pkg/skaffold/runner/renderer.go b/pkg/skaffold/runner/renderer.go
new file mode 100644
index 000000000..6a3771d84
--- /dev/null
+++ b/pkg/skaffold/runner/renderer.go
@@ -0,0 +1,39 @@
+/*
+Copyright 2022 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"
+
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
+	runcontext "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext/v2"
+)
+
+// GetRenderer creates a renderer from a given RunContext and pipeline definitions.
+func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDir string, labels map[string]string, usingLegacyHelmDeploy bool) (renderer.Renderer, error) {
+	rs := runCtx.Renderers()
+
+	var renderers []renderer.Renderer
+	for _, r := range rs {
+		r, err := renderer.New(runCtx, r, hydrationDir, labels, usingLegacyHelmDeploy)
+		if err != nil {
+			return nil, err
+		}
+		renderers = append(renderers, r)
+	}
+	return renderer.NewRenderMux(renderers), nil
+}
diff --git a/pkg/skaffold/runner/runcontext/v2/context.go b/pkg/skaffold/runner/runcontext/v2/context.go
index f900b0df1..58921bb44 100644
--- a/pkg/skaffold/runner/runcontext/v2/context.go
+++ b/pkg/skaffold/runner/runcontext/v2/context.go
@@ -85,6 +85,7 @@ func (ps Pipelines) Artifacts() []*latest.Artifact {
 	return artifacts
 }
 
+// TODO: Clean up code duplication
 func (ps Pipelines) DeployConfigs() []latest.DeployConfig {
 	var cfgs []latest.DeployConfig
 	for _, p := range ps.pipelines {
@@ -101,6 +102,14 @@ func (ps Pipelines) Deployers() []latest.DeployConfig {
 	return deployers
 }
 
+func (ps Pipelines) Renderers() []latest.RenderConfig {
+	var rcs []latest.RenderConfig
+	for _, p := range ps.pipelines {
+		rcs = append(rcs, p.Render)
+	}
+	return rcs
+}
+
 func (ps Pipelines) TestCases() []*latest.TestCase {
 	var tests []*latest.TestCase
 	for _, p := range ps.pipelines {
@@ -165,6 +174,8 @@ func (rc *RunContext) DeployConfigs() []latest.DeployConfig { return rc.Pipeline
 
 func (rc *RunContext) Deployers() []latest.DeployConfig { return rc.Pipelines.Deployers() }
 
+func (rc *RunContext) Renderers() []latest.RenderConfig { return rc.Pipelines.Renderers() }
+
 func (rc *RunContext) TestCases() []*latest.TestCase { return rc.Pipelines.TestCases() }
 
 func (rc *RunContext) StatusCheckDeadlineSeconds() int {
diff --git a/pkg/skaffold/runner/timings.go b/pkg/skaffold/runner/timings.go
index ac999e65c..06996ddd0 100644
--- a/pkg/skaffold/runner/timings.go
+++ b/pkg/skaffold/runner/timings.go
@@ -85,13 +85,13 @@ func (w withTimings) Test(ctx context.Context, out io.Writer, builds []graph.Art
 
 func (w withTimings) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool, filepath string) (manifest.ManifestList, error) {
 	start := time.Now()
-	output.Default.Fprintln(out, "Starting render...")
+	log.Entry(ctx).Infoln("Starting render...")
 
 	manifestsLists, err := w.Renderer.Render(ctx, out, builds, offline, filepath)
 	if err != nil {
 		return nil, err
 	}
-	log.Entry(context.TODO()).Infoln("Render completed in", timeutil.Humanize(time.Since(start)))
+	log.Entry(ctx).Infoln("Render completed in", timeutil.Humanize(time.Since(start)))
 	return manifestsLists, nil
 }
 
diff --git a/pkg/skaffold/runner/v2/new.go b/pkg/skaffold/runner/v2/new.go
index 951dc5746..681d044bc 100644
--- a/pkg/skaffold/runner/v2/new.go
+++ b/pkg/skaffold/runner/v2/new.go
@@ -32,7 +32,6 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
-	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
 	runcontext "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext/v2"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
@@ -82,7 +81,7 @@ func NewForConfig(ctx context.Context, runCtx *runcontext.RunContext) (*Skaffold
 		return nil, fmt.Errorf("getting render output path: %w", err)
 	}
 
-	renderer, err := renderer.New(runCtx, hydrationDir, labeller.Labels(), runCtx.UsingLegacyHelmDeploy())
+	renderer, err := runner.GetRenderer(ctx, runCtx, hydrationDir, labeller.Labels(), runCtx.UsingLegacyHelmDeploy())
 	if err != nil {
 		endTrace(instrumentation.TraceEndError(err))
 		return nil, fmt.Errorf("creating renderer: %w", err)
diff --git a/pkg/skaffold/runner/v2/render.go b/pkg/skaffold/runner/v2/render.go
index d5817f549..3b643894d 100644
--- a/pkg/skaffold/runner/v2/render.go
+++ b/pkg/skaffold/runner/v2/render.go
@@ -34,7 +34,8 @@ func (r *SkaffoldRunner) Render(ctx context.Context, out io.Writer, builds []gra
 		// Fetch the digest and append it to the tag with the format of "tag@digest"
 		if r.runCtx.DigestSource() == constants.RemoteDigestSource {
 			for i, a := range builds {
-				digest, err := docker.RemoteDigest(a.Tag, r.runCtx)
+				// remote digest to platform dependant build not supported
+				digest, err := docker.RemoteDigest(a.Tag, r.runCtx, nil)
 				if err != nil {
 					return nil, fmt.Errorf("failed to resolve the digest of %s: does the image exist remotely?", a.Tag)
 				}
diff --git a/pkg/skaffold/util/platform.go b/pkg/skaffold/util/platform.go
new file mode 100644
index 000000000..a572afe8f
--- /dev/null
+++ b/pkg/skaffold/util/platform.go
@@ -0,0 +1,26 @@
+/*
+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 util
+
+import (
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+func ConvertToV1Platform(platform specs.Platform) *v1.Platform {
+	return &v1.Platform{Architecture: platform.Architecture, OS: platform.OS, OSVersion: platform.OSVersion, OSFeatures: platform.OSFeatures, Variant: platform.Variant}
+}
diff --git a/pkg/webhook/constants/constants.go b/pkg/webhook/constants/constants.go
index bdf953498..3c069687d 100644
--- a/pkg/webhook/constants/constants.go
+++ b/pkg/webhook/constants/constants.go
@@ -16,6 +16,15 @@ limitations under the License.
 
 package constants
 
+import "fmt"
+
+// DocsVersion defines the version of the docs currently ""(for v1) and "-v2"(for v2)
+// This variable is injected at build time based on what version of the controller is being built
+var DocsVersion string
+
+// DocsLabel kicks off the controller when added to a PR
+var DocsLabel = fmt.Sprintf("docs-modifications%s", DocsVersion)
+
 const (
 	// GithubAccessToken is the env variable auth for container-tools-bot is stored in
 	GithubAccessToken = "GITHUB_ACCESS_TOKEN"
@@ -35,9 +44,6 @@ const (
 	// when a PR is labeled
 	LabeledAction = "labeled"
 
-	// DocsLabel kicks off the controller when added to a PR
-	DocsLabel = "docs-modifications"
-
 	// Namespace is the namespace deployments and services will be created in
 	Namespace = "default"
 
diff --git a/pkg/webhook/kubernetes/deployment.go b/pkg/webhook/kubernetes/deployment.go
index 599fe1417..add4cab3d 100644
--- a/pkg/webhook/kubernetes/deployment.go
+++ b/pkg/webhook/kubernetes/deployment.go
@@ -93,7 +93,7 @@ func CreateDeployment(pr *github.PullRequestEvent, svc *v1.Service, externalIP s
 						{
 							Name:       "server",
 							Image:      constants.DeploymentImage,
-							Args:       []string{"deploy/docs/preview.sh", BaseURL(externalIP)},
+							Args:       []string{fmt.Sprintf("deploy/docs%s/preview.sh", constants.DocsVersion), BaseURL(externalIP)},
 							WorkingDir: repoPath,
 							VolumeMounts: []v1.VolumeMount{
 								{
diff --git a/webhook/webhook.go b/webhook/webhook.go
index 4d5a553f0..7a2917a0d 100644
--- a/webhook/webhook.go
+++ b/webhook/webhook.go
@@ -42,7 +42,7 @@ func main() {
 	http.HandleFunc("/receive", handleGithubEvent)
 	flag.Parse()
 	// Start the server
-	log.Println("Listening...")
+	log.Printf("Listening%s...\n", constants.DocsVersion)
 	log.Fatal(http.ListenAndServe(port, nil))
 }
 


Test patch:
diff --git a/integration/debug_test.go b/integration/debug_test.go
index 66e26e718..158f3bfc7 100644
--- a/integration/debug_test.go
+++ b/integration/debug_test.go
@@ -27,13 +27,11 @@ import (
 
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types"
+	"github.com/GoogleContainerTools/skaffold/proto/v1"
 	"github.com/GoogleContainerTools/skaffold/testutil"
 )
 
 func TestDebug(t *testing.T) {
-	// TODO: This test shall pass once render v2 is completed.
-	t.SkipNow()
-
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 
 	tests := []struct {
@@ -123,8 +121,6 @@ func TestDebug(t *testing.T) {
 }
 
 func TestDockerDebug(t *testing.T) {
-	// TODO: fix https://github.com/GoogleContainerTools/skaffold/issues/7030
-	t.Skipf("Fix as https://github.com/GoogleContainerTools/skaffold/issues/7030")
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 
 	t.Run("debug docker deployment", func(t *testing.T) {
@@ -197,9 +193,6 @@ func checkSupportContainer(containers []dockertypes.Container, found *bool) {
 }
 
 func TestFilterWithDebugging(t *testing.T) {
-	// TODO: This test shall pass once render v2 is completed.
-	t.SkipNow()
-
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 	// `filter` currently expects to receive a digested yaml
 	renderedOutput := skaffold.Render("--digest-source=local").InDir("examples/getting-started").RunOrFailOutput(t)
@@ -222,11 +215,7 @@ func TestFilterWithDebugging(t *testing.T) {
 	})
 }
 
-/*
 func TestDebugEventsRPC_StatusCheck(t *testing.T) {
-	// TODO: This test shall pass once render v2 is completed.
-	t.SkipNow()
-
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 
 	// Run skaffold build first to fail quickly on a build failure
@@ -241,9 +230,6 @@ func TestDebugEventsRPC_StatusCheck(t *testing.T) {
 }
 
 func TestDebugEventsRPC_NoStatusCheck(t *testing.T) {
-	// TODO: This test shall pass once render v2 is completed.
-	t.SkipNow()
-
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 
 	// Run skaffold build first to fail quickly on a build failure
@@ -277,4 +263,3 @@ func waitForDebugEvent(t *testing.T, client *NSKubernetesClient, rpcAddr string)
 		}
 	}
 }
-*/
diff --git a/integration/dev_test.go b/integration/dev_test.go
index b8b47a65f..f7f8dd7ff 100644
--- a/integration/dev_test.go
+++ b/integration/dev_test.go
@@ -386,7 +386,7 @@ func waitForPortForwardEvent(t *testing.T, entries chan *proto.LogEntry, resourc
 func assertResponseFromPort(t *testing.T, address string, port int, expected string) {
 	url := fmt.Sprintf("http://%s:%d", address, port)
 	t.Logf("Waiting on %s to return: %s", url, expected)
-	ctx, cancelTimeout := context.WithTimeout(context.Background(), 2*time.Minute)
+	ctx, cancelTimeout := context.WithTimeout(context.Background(), 4*time.Minute)
 	defer cancelTimeout()
 
 	for {
diff --git a/integration/modules_test.go b/integration/modules_test.go
index cdc3cb5da..760650c56 100644
--- a/integration/modules_test.go
+++ b/integration/modules_test.go
@@ -26,9 +26,6 @@ import (
 )
 
 func TestModules_BuildDependency(t *testing.T) {
-	// TODO: Fix https://github.com/GoogleContainerTools/skaffold/issues/7035.
-	t.Skipf("Fix https://github.com/GoogleContainerTools/skaffold/issues/7035")
-
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 	t.Run("build dependency between artifacts of different modules", func(t *testing.T) {
 		ns, client := SetupNamespace(t)
diff --git a/integration/render_test.go b/integration/render_test.go
index b5640115e..3c045ad96 100644
--- a/integration/render_test.go
+++ b/integration/render_test.go
@@ -73,14 +73,12 @@ spec:
 		tmpDir := t.NewTempDir()
 		tmpDir.Write("deployment.yaml", test.input).Chdir()
 
-		mockCfg := mockConfig{
-			renderConfig: &latest.RenderConfig{
-				Generate: latest.Generate{
-					RawK8s: []string{"deployment.yaml"}},
-			},
-			workingDir: tmpDir.Root(),
+		rc := latest.RenderConfig{
+			Generate: latest.Generate{
+				RawK8s: []string{"deployment.yaml"}},
 		}
-		r, err := kubectl.New(mockCfg, map[string]string{})
+		mockCfg := mockConfig{workingDir: tmpDir.Root()}
+		r, err := kubectl.New(mockCfg, rc, map[string]string{})
 		t.RequireNoError(err)
 		var b bytes.Buffer
 		l, err := r.Render(context.Background(), &b, test.builds, false, test.renderPath)
@@ -214,15 +212,12 @@ spec:
 			tmpDir := t.NewTempDir()
 			tmpDir.Write("deployment.yaml", test.input).
 				Chdir()
-
-			mockCfg := mockConfig{
-				renderConfig: &latest.RenderConfig{
-					Generate: latest.Generate{
-						RawK8s: []string{"deployment.yaml"}},
-				},
-				workingDir: tmpDir.Root(),
+			rc := latest.RenderConfig{
+				Generate: latest.Generate{
+					RawK8s: []string{"deployment.yaml"}},
 			}
-			r, err := kubectl.New(mockCfg, map[string]string{})
+			mockCfg := mockConfig{workingDir: tmpDir.Root()}
+			r, err := kubectl.New(mockCfg, rc, map[string]string{})
 			t.RequireNoError(err)
 			var b bytes.Buffer
 			l, err := r.Render(context.Background(), &b, test.builds, false, "")
@@ -753,11 +748,9 @@ spec:
 }
 
 type mockConfig struct {
-	renderConfig *latest.RenderConfig
-	workingDir   string
+	workingDir string
 }
 
-func (mc mockConfig) GetRenderConfig() *latest.RenderConfig       { return mc.renderConfig }
 func (mc mockConfig) GetWorkingDir() string                       { return mc.workingDir }
 func (mc mockConfig) TransformAllowList() []latest.ResourceFilter { return nil }
 func (mc mockConfig) TransformDenyList() []latest.ResourceFilter  { return nil }
diff --git a/integration/testdata/debug/skaffold.yaml b/integration/testdata/debug/skaffold.yaml
index 2b6271ae1..cad5f9fc0 100644
--- a/integration/testdata/debug/skaffold.yaml
+++ b/integration/testdata/debug/skaffold.yaml
@@ -17,10 +17,8 @@ build:
     context: go
   #- image: skaffold-debug-netcore
   #  context: netcore
-
-deploy:
-  kubectl:
-    manifests:
+manifests:
+  rawYaml:
     - java/k8s/web.yaml
     - nodejs/k8s/pod.yaml
     - npm/k8s/pod.yaml
@@ -31,8 +29,6 @@ deploy:
 profiles:
 - name: kustomize
   patches:
-  - op: remove
-    path: /deploy/kubectl
   deploy:
     kustomize: {}
 # use GCP Buildpacks to build the individual projects
@@ -65,8 +61,6 @@ profiles:
     #    builder: "gcr.io/buildpacks/builder:v1"
 - name: docker
   patches:
-  - op: remove
-    path: /deploy/kubectl
   build:
     artifacts:
     - image: skaffold-debug-nodejs
diff --git a/integration/testdata/modules/app1/skaffold.yaml b/integration/testdata/modules/app1/skaffold.yaml
index 38f1fed9e..b397b2a4b 100644
--- a/integration/testdata/modules/app1/skaffold.yaml
+++ b/integration/testdata/modules/app1/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v2beta25
+apiVersion: skaffold/v3alpha1
 kind: Config
 requires:
   - path: ../app2
@@ -16,7 +16,6 @@ build:
     requires:
     - image: image2
 
-deploy:
-  kubectl:
-    manifests:
-    - 'k8s.yaml'
+manifests:
+  rawYaml:
+  - 'k8s.yaml'
diff --git a/integration/testdata/modules/app2/skaffold.yaml b/integration/testdata/modules/app2/skaffold.yaml
index 82bf4eb30..659f45bf7 100644
--- a/integration/testdata/modules/app2/skaffold.yaml
+++ b/integration/testdata/modules/app2/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v2beta25
+apiVersion: skaffold/v3alpha1
 kind: Config
 requires:
   - path: ../app3
@@ -17,7 +17,6 @@ build:
     - image: image3
       alias: IMAGE3
 
-deploy:
-  kubectl:
-    manifests:
-    - 'k8s.yaml'
+manifests:
+  rawYaml:
+  - 'k8s.yaml'
diff --git a/integration/testdata/modules/app3/skaffold.yaml b/integration/testdata/modules/app3/skaffold.yaml
index adbd0887a..240f278b0 100644
--- a/integration/testdata/modules/app3/skaffold.yaml
+++ b/integration/testdata/modules/app3/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v2beta25
+apiVersion: skaffold/v3alpha1
 kind: Config
 build:
   tagPolicy:
@@ -12,7 +12,6 @@ build:
         SLEEP: "2"
         FAIL: "0"
 
-deploy:
-  kubectl:
-    manifests:
-    - 'k8s.yaml'
+manifests:
+  rawYaml:
+  - 'k8s.yaml'
diff --git a/integration/testdata/modules/skaffold.yaml b/integration/testdata/modules/skaffold.yaml
index 4860f1533..c014ec69d 100644
--- a/integration/testdata/modules/skaffold.yaml
+++ b/integration/testdata/modules/skaffold.yaml
@@ -1,6 +1,6 @@
-apiVersion: skaffold/v3beta2
+apiVersion: skaffold/v3alpha1
 kind: Config
 requires:
   - path: app1
   - path: app2
-  - path: app3
+  - path: app3
\ No newline at end of file
diff --git a/pkg/skaffold/build/cache/lookup_test.go b/pkg/skaffold/build/cache/lookup_test.go
index 16931decf..bd6cef8b8 100644
--- a/pkg/skaffold/build/cache/lookup_test.go
+++ b/pkg/skaffold/build/cache/lookup_test.go
@@ -24,6 +24,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/client"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
@@ -196,7 +197,7 @@ func TestLookupRemote(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config) (string, error) {
+			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config, _ *v1.Platform) (string, error) {
 				switch {
 				case identifier == "tag":
 					return "digest", nil
diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go
index a4bd625c5..afc323a25 100644
--- a/pkg/skaffold/build/cache/retrieve_test.go
+++ b/pkg/skaffold/build/cache/retrieve_test.go
@@ -24,6 +24,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
@@ -218,7 +219,7 @@ func TestCacheBuildRemote(t *testing.T) {
 			return dockerDaemon, nil
 		})
 		t.Override(&docker.DefaultAuthHelper, stubAuth{})
-		t.Override(&docker.RemoteDigest, func(ref string, _ docker.Config) (string, error) {
+		t.Override(&docker.RemoteDigest, func(ref string, _ docker.Config, _ *v1.Platform) (string, error) {
 			switch ref {
 			case "artifact1:tag1":
 				return "sha256:51ae7fa00c92525c319404a3a6d400e52ff9372c5a39cb415e0486fe425f3165", nil
@@ -303,7 +304,7 @@ func TestCacheFindMissing(t *testing.T) {
 			return dockerDaemon, nil
 		})
 		t.Override(&docker.DefaultAuthHelper, stubAuth{})
-		t.Override(&docker.RemoteDigest, func(ref string, _ docker.Config) (string, error) {
+		t.Override(&docker.RemoteDigest, func(ref string, _ docker.Config, _ *v1.Platform) (string, error) {
 			switch ref {
 			case "artifact1:tag1":
 				return "sha256:51ae7fa00c92525c319404a3a6d400e52ff9372c5a39cb415e0486fe425f3165", nil
diff --git a/pkg/skaffold/build/jib/gradle_test.go b/pkg/skaffold/build/jib/gradle_test.go
index 9ddefea44..06013c9a1 100644
--- a/pkg/skaffold/build/jib/gradle_test.go
+++ b/pkg/skaffold/build/jib/gradle_test.go
@@ -28,6 +28,7 @@ import (
 	"testing"
 	"time"
 
+	cv1 "github.com/google/go-containerregistry/pkg/v1"
 	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
@@ -149,7 +150,7 @@ func TestBuildJibGradleToRegistry(t *testing.T) {
 			t.NewTempDir().Touch("build.gradle").Chdir()
 			t.Override(&gradleBuildArgsFunc, getGradleBuildArgsFuncFake(t, MinimumJibGradleVersion))
 			t.Override(&util.DefaultExecCommand, test.commands)
-			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config) (string, error) {
+			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config, _ *cv1.Platform) (string, error) {
 				if identifier == "img:tag" {
 					return "digest", nil
 				}
diff --git a/pkg/skaffold/build/jib/maven_test.go b/pkg/skaffold/build/jib/maven_test.go
index 95c426f27..4e981da48 100644
--- a/pkg/skaffold/build/jib/maven_test.go
+++ b/pkg/skaffold/build/jib/maven_test.go
@@ -28,6 +28,7 @@ import (
 	"testing"
 	"time"
 
+	cv1 "github.com/google/go-containerregistry/pkg/v1"
 	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
@@ -143,7 +144,7 @@ func TestBuildJibMavenToRegistry(t *testing.T) {
 			t.Override(&mavenBuildArgsFunc, getMavenBuildArgsFuncFake(t, MinimumJibMavenVersion))
 			t.NewTempDir().Touch("pom.xml").Chdir()
 			t.Override(&util.DefaultExecCommand, test.commands)
-			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config) (string, error) {
+			t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config, _ *cv1.Platform) (string, error) {
 				if identifier == "img:tag" {
 					return "digest", nil
 				}
diff --git a/pkg/skaffold/deploy/deploy_mux_test.go b/pkg/skaffold/deploy/deploy_mux_test.go
index f3e94d3f8..ce8de0d1a 100644
--- a/pkg/skaffold/deploy/deploy_mux_test.go
+++ b/pkg/skaffold/deploy/deploy_mux_test.go
@@ -228,69 +228,3 @@ func TestDeployerMux_Dependencies(t *testing.T) {
 		})
 	}
 }
-
-// TODO: Add RendererMux https://github.com/GoogleContainerTools/skaffold/issues/7330
-// func TestDeployerMux_Render(t *testing.T) {
-//	tests := []struct {
-//		name           string
-//		render1        string
-//		render2        string
-//		err1           error
-//		err2           error
-//		expectedRender string
-//		shouldErr      bool
-//	}{
-//		{
-//			name:           "concatenates render results with separator",
-//			render1:        "manifest-1",
-//			render2:        "manifest-2",
-//			expectedRender: "manifest-1\n---\nmanifest-2\n",
-//		},
-//		{
-//			name:      "short-circuits when first call fails",
-//			render1:   "manifest-1",
-//			err1:      fmt.Errorf("failed in first"),
-//			render2:   "manifest-2",
-//			shouldErr: true,
-//		},
-//		{
-//			name:      "short-circuits when second call fails",
-//			render1:   "manifest-1",
-//			render2:   "manifest-2",
-//			err2:      fmt.Errorf("failed in first"),
-//			shouldErr: true,
-//		},
-//	}
-//
-//	for _, test := range tests {
-//		t.Run("output to writer "+test.name, func(t *testing.T) {
-//			deployerMux := NewDeployerMux([]Deployer{
-//				NewMockDeployer().WithRenderResult(test.render1).WithRenderErr(test.err1),
-//				NewMockDeployer().WithRenderResult(test.render2).WithRenderErr(test.err2),
-//			}, false)
-//
-//			buf := &bytes.Buffer{}
-//			err := deployerMux.Render(context.Background(), buf, nil, true, "")
-//			testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expectedRender, buf.String())
-//		})
-//	}
-//
-//	t.Run("output to file", func(t *testing.T) {
-//		// only check the good case here
-//		test := tests[0]
-//
-//		tmpDir := testutil.NewTempDir(t)
-//
-//		deployerMux := NewDeployerMux([]Deployer{
-//			NewMockDeployer().WithRenderResult(test.render1).WithRenderErr(test.err1),
-//			NewMockDeployer().WithRenderResult(test.render2).WithRenderErr(test.err2),
-//		}, false)
-//
-//		err := deployerMux.Render(context.Background(), nil, nil, true, tmpDir.Path("render"))
-//		testutil.CheckError(t, false, err)
-//
-//		file, _ := os.Open(tmpDir.Path("render"))
-//		content, _ := ioutil.ReadAll(file)
-//		testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expectedRender, string(content))
-//	})
-//}
diff --git a/pkg/skaffold/deploy/kubectl/kubectl_test.go b/pkg/skaffold/deploy/kubectl/kubectl_test.go
index 7b2638668..2df04c4e4 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl_test.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl_test.go
@@ -238,14 +238,15 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			}, &label.DefaultLabeller{}, &test.kubectl, filepath.Join(tmpDir.Root(), constants.DefaultHydrationDir))
 			t.RequireNoError(err)
 
+			rc := latest.RenderConfig{Generate: test.generate}
 			mockCfg := &kubectlConfig{
 				RunContext: runcontext.RunContext{
 					WorkingDir: tmpDir.Root(),
 					Pipelines: runcontext.NewPipelines([]latest.Pipeline{
-						{Render: latest.RenderConfig{Generate: test.generate}}}),
+						{Render: rc}}),
 				},
 			}
-			r, err := kubectlR.New(mockCfg, map[string]string{})
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{})
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -328,15 +329,15 @@ func TestKubectlCleanup(t *testing.T) {
 				RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: TestNamespace}},
 			}, &label.DefaultLabeller{}, &test.kubectl, filepath.Join(tmpDir.Root(), constants.DefaultHydrationDir))
 			t.RequireNoError(err)
-
+			rc := latest.RenderConfig{Generate: test.generate}
 			mockCfg := &kubectlConfig{
 				RunContext: runcontext.RunContext{
 					WorkingDir: tmpDir.Root(),
 					Pipelines: runcontext.NewPipelines([]latest.Pipeline{
-						{Render: latest.RenderConfig{Generate: test.generate}}}),
+						{Render: rc}}),
 				},
 			}
-			r, err := kubectlR.New(mockCfg, map[string]string{})
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{})
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -626,13 +627,14 @@ func TestGCSManifests(t *testing.T) {
 			if err := ioutil.WriteFile(manifest.ManifestTmpDir+"/deployment.yaml", []byte(DeploymentWebYAML), os.ModePerm); err != nil {
 				t.Fatal(err)
 			}
+			rc := latest.RenderConfig{Generate: test.generate}
 			mockCfg := &kubectlConfig{
 				RunContext: runcontext.RunContext{
 					Pipelines: runcontext.NewPipelines([]latest.Pipeline{
 						{Render: latest.RenderConfig{Generate: test.generate}}}),
 				},
 			}
-			r, err := kubectlR.New(mockCfg, map[string]string{})
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{})
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
diff --git a/pkg/skaffold/docker/remote_test.go b/pkg/skaffold/docker/remote_test.go
index 07df8d916..970ac202e 100644
--- a/pkg/skaffold/docker/remote_test.go
+++ b/pkg/skaffold/docker/remote_test.go
@@ -90,7 +90,7 @@ func TestRemoteImage(t *testing.T) {
 
 			img, err := getRemoteImage(test.image, &mockConfig{
 				insecureRegistries: test.insecureRegistries,
-			})
+			}, nil)
 
 			t.CheckError(test.shouldErr, err)
 			if !test.shouldErr {
@@ -145,7 +145,7 @@ func TestRemoteDigest(t *testing.T) {
 				}, nil
 			})
 
-			digest, err := RemoteDigest(test.image, &mockConfig{})
+			digest, err := RemoteDigest(test.image, &mockConfig{}, nil)
 
 			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedDigest, digest)
 		})
diff --git a/pkg/skaffold/platform/resolver_test.go b/pkg/skaffold/platform/resolver_test.go
index 428ca4a30..7bb06e527 100644
--- a/pkg/skaffold/platform/resolver_test.go
+++ b/pkg/skaffold/platform/resolver_test.go
@@ -67,8 +67,11 @@ func TestResolver(t *testing.T) {
 			pipelines: []latest.Pipeline{{Build: latest.BuildConfig{
 				Platforms: []string{"windows/amd64"},
 				Artifacts: []*latest.Artifact{{ImageName: "img1"}, {ImageName: "img2"}}}}},
-			runMode:   config.RunModes.Dev,
-			shouldErr: true,
+			runMode: config.RunModes.Dev,
+			expected: map[string]Matcher{
+				"img1": {Platforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}, {OS: "linux", Architecture: "386"}}},
+				"img2": {Platforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}, {OS: "linux", Architecture: "386"}}},
+			},
 		},
 		{
 			description:      "cluster platform selected for `dev` mode",
diff --git a/pkg/skaffold/render/renderer/kpt/kpt_test.go b/pkg/skaffold/render/renderer/kpt/kpt_test.go
index 268c44c60..4a17bb354 100644
--- a/pkg/skaffold/render/renderer/kpt/kpt_test.go
+++ b/pkg/skaffold/render/renderer/kpt/kpt_test.go
@@ -62,14 +62,14 @@ metadata:
 func TestRender(t *testing.T) {
 	tests := []struct {
 		description     string
-		renderConfig    *latest.RenderConfig
+		renderConfig    latest.RenderConfig
 		config          *runcontext.RunContext
 		originalKptfile string
 		updatedKptfile  string
 	}{
 		{
 			description: "single manifest, no hydration rule",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
 			},
 			originalKptfile: initKptfile,
@@ -82,7 +82,7 @@ pipeline: {}
 		},
 		{
 			description: "manifests with validation rule.",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
 				Validate: &[]latest.Validator{{Name: "kubeval"}},
 			},
@@ -98,7 +98,7 @@ pipeline:
 		},
 		{
 			description: "manifests with updated validation rule.",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
 				Validate: &[]latest.Validator{{Name: "kubeval"}},
 			},
@@ -121,7 +121,7 @@ pipeline:
 		},
 		{
 			description: "manifests with transformation rule.",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate:  latest.Generate{RawK8s: []string{"pod.yaml"}},
 				Transform: &[]latest.Transformer{{Name: "set-labels", ConfigMap: []string{"owner:tester"}}},
 			},
@@ -139,7 +139,7 @@ pipeline:
 		},
 		{
 			description: "manifests with updated transformation rule.",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate:  latest.Generate{RawK8s: []string{"pod.yaml"}},
 				Transform: &[]latest.Transformer{{Name: "set-labels", ConfigMap: []string{"owner:tester"}}},
 			},
@@ -171,10 +171,9 @@ pipeline:
 				Touch("empty.ignored").
 				Chdir()
 			mockCfg := mockConfig{
-				renderConfig: test.renderConfig,
-				workingDir:   tmpDirObj.Root(),
+				workingDir: tmpDirObj.Root(),
 			}
-			r, err := New(mockCfg, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{})
+			r, err := New(mockCfg, test.renderConfig, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{})
 			t.CheckNoError(err)
 			t.Override(&util.DefaultExecCommand,
 				testutil.CmdRun(fmt.Sprintf("kpt fn render %v",
@@ -236,14 +235,14 @@ inventory:
 			tmpDirObj.Write("pod.yaml", podYaml).
 				Write(filepath.Join(constants.DefaultHydrationDir, kptfile.KptFileName), test.originalKptfile).
 				Chdir()
+			renderConfig := latest.RenderConfig{
+				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
+				Validate: &[]latest.Validator{{Name: "kubeval"}},
+			}
 			mockCfg := mockConfig{
-				renderConfig: &latest.RenderConfig{
-					Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
-					Validate: &[]latest.Validator{{Name: "kubeval"}},
-				},
 				workingDir: tmpDirObj.Root(),
 			}
-			r, err := New(mockCfg, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{})
+			r, err := New(mockCfg, renderConfig, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{})
 			t.CheckNoError(err)
 			t.Override(&util.DefaultExecCommand,
 				testutil.CmdRun(fmt.Sprintf("kpt fn render %v",
@@ -259,11 +258,9 @@ inventory:
 }
 
 type mockConfig struct {
-	renderConfig *latest.RenderConfig
-	workingDir   string
+	workingDir string
 }
 
-func (mc mockConfig) GetRenderConfig() *latest.RenderConfig       { return mc.renderConfig }
 func (mc mockConfig) GetWorkingDir() string                       { return mc.workingDir }
 func (mc mockConfig) TransformAllowList() []latest.ResourceFilter { return nil }
 func (mc mockConfig) TransformDenyList() []latest.ResourceFilter  { return nil }
diff --git a/pkg/skaffold/render/renderer/kubectl/kubectl_test.go b/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
index 033c45e7a..35b850a94 100644
--- a/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
+++ b/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
@@ -60,20 +60,20 @@ spec:
 func TestRender(t *testing.T) {
 	tests := []struct {
 		description  string
-		renderConfig *latest.RenderConfig
+		renderConfig latest.RenderConfig
 		labels       map[string]string
 		expected     string
 	}{
 		{
 			description: "single manifest with no labels",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
 			},
 			expected: taggedPodYaml,
 		},
 		{
 			description: "single manifest with labels",
-			renderConfig: &latest.RenderConfig{
+			renderConfig: latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
 			},
 			labels:   map[string]string{"run.id": "test"},
@@ -86,11 +86,8 @@ func TestRender(t *testing.T) {
 			tmpDirObj.Write("pod.yaml", podYaml).
 				Touch("empty.ignored").
 				Chdir()
-			mockCfg := mockConfig{
-				renderConfig: test.renderConfig,
-				workingDir:   tmpDirObj.Root(),
-			}
-			r, err := New(mockCfg, test.labels)
+			mockCfg := mockConfig{workingDir: tmpDirObj.Root()}
+			r, err := New(mockCfg, test.renderConfig, test.labels)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			manifestList, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -102,11 +99,9 @@ func TestRender(t *testing.T) {
 }
 
 type mockConfig struct {
-	renderConfig *latest.RenderConfig
-	workingDir   string
+	workingDir string
 }
 
-func (mc mockConfig) GetRenderConfig() *latest.RenderConfig       { return mc.renderConfig }
 func (mc mockConfig) GetWorkingDir() string                       { return mc.workingDir }
 func (mc mockConfig) TransformAllowList() []latest.ResourceFilter { return nil }
 func (mc mockConfig) TransformDenyList() []latest.ResourceFilter  { return nil }
diff --git a/pkg/skaffold/render/renderer/render_mux_test.go b/pkg/skaffold/render/renderer/render_mux_test.go
new file mode 100644
index 000000000..32bde4088
--- /dev/null
+++ b/pkg/skaffold/render/renderer/render_mux_test.go
@@ -0,0 +1,101 @@
+/*
+Copyright 2022 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 renderer
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"io"
+	"testing"
+
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/testutil"
+	testEvent "github.com/GoogleContainerTools/skaffold/testutil/event"
+)
+
+func TestRenderMux_Render(t *testing.T) {
+	tests := []struct {
+		name         string
+		renderers    []Renderer
+		expected     string
+		expectedDeps []string
+		shouldErr    bool
+	}{
+		{
+			name: "concatenates render results with separator",
+			renderers: []Renderer{
+				mock{manifests: "manifest-1", deps: []string{"file1.txt", "file2.txt"}},
+				mock{manifests: "manifest-2", deps: []string{"file2.txt", "file3.txt"}}},
+			expected:     "manifest-1\n---\nmanifest-2",
+			expectedDeps: []string{"file1.txt", "file2.txt", "file3.txt"},
+		},
+		{
+			name: "short-circuits when first call fails",
+			renderers: []Renderer{
+				mock{manifests: "manifest-1", deps: []string{"file1.txt"}},
+				mock{deps: []string{"file2.txt"}, shouldErr: true}},
+			expectedDeps: []string{"file1.txt", "file2.txt"},
+			shouldErr:    true,
+		},
+		{
+			name: "short-circuits when second call fails",
+			renderers: []Renderer{
+				mock{deps: []string{"file1.txt"}, shouldErr: true},
+				mock{manifests: "manifest-2", deps: []string{"file2.txt"}}},
+			expectedDeps: []string{"file1.txt", "file2.txt"},
+			shouldErr:    true,
+		},
+	}
+	for _, tc := range tests {
+		testutil.Run(t, tc.name, func(t *testutil.T) {
+			testEvent.InitializeState([]latest.Pipeline{{
+				Deploy: latest.DeployConfig{},
+				Build: latest.BuildConfig{
+					BuildType: latest.BuildType{
+						LocalBuild: &latest.LocalBuild{},
+					},
+				}}})
+			mux := NewRenderMux(tc.renderers)
+			buf := &bytes.Buffer{}
+			actual, err := mux.Render(context.Background(), buf, nil, true, "")
+			t.CheckErrorAndDeepEqual(tc.shouldErr, err, tc.expected, actual.String())
+			actualDeps, errD := mux.ManifestDeps()
+			t.CheckNoError(errD)
+			t.CheckDeepEqual(tc.expectedDeps, actualDeps)
+		})
+	}
+}
+
+type mock struct {
+	manifests string
+	deps      []string
+	shouldErr bool
+}
+
+func (m mock) ManifestDeps() ([]string, error) {
+	return m.deps, nil
+}
+
+func (m mock) Render(context.Context, io.Writer, []graph.Artifact, bool, string) (manifest.ManifestList, error) {
+	if m.shouldErr {
+		return nil, fmt.Errorf("render error")
+	}
+	return manifest.Load(bytes.NewReader([]byte(m.manifests)))
+}
diff --git a/pkg/skaffold/runner/timings_test.go b/pkg/skaffold/runner/timings_test.go
index 5abf9b439..0f41fd02f 100644
--- a/pkg/skaffold/runner/timings_test.go
+++ b/pkg/skaffold/runner/timings_test.go
@@ -250,7 +250,7 @@ func TestTimingsRender(t *testing.T) {
 
 			t.CheckError(test.shouldErr, err)
 			t.CheckMatches(test.shouldOutput, out.String())
-			t.CheckMatches(test.shouldLog, lastInfoEntry(hook))
+			t.CheckMatches(test.shouldLog, entryAtIndex(hook, 1))
 		})
 	}
 }
@@ -337,3 +337,11 @@ func lastInfoEntry(hook *logrustest.Hook) string {
 	}
 	return ""
 }
+
+func entryAtIndex(hook *logrustest.Hook, i int) string {
+	e := hook.AllEntries()
+	if len(e) > i {
+		return e[i].Message
+	}
+	return ""
+}