Repository: GoogleContainerTools/skaffold
Pull request #7663 (2022-07-20 21:55:14)

fix: Properly clean up and restart port forwarding for Cloud Run

**Description**
Cloud Run port forwarding was previously adding a new port forward record every time it redeployed, so you'd end up with many port forwards for the same service as you updated your code with skaffold dev. Dedupe the port forwards for the same resource so we only start one.



Patch:
diff --git a/cmd/skaffold/app/cmd/delete.go b/cmd/skaffold/app/cmd/delete.go
index 3a0285529..a2e350ab9 100644
--- a/cmd/skaffold/app/cmd/delete.go
+++ b/cmd/skaffold/app/cmd/delete.go
@@ -18,12 +18,11 @@ package cmd
 
 import (
 	"context"
-	"fmt"
 	"io"
 
 	"github.com/spf13/cobra"
 
-	deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
 )
@@ -46,10 +45,10 @@ func NewCmdDelete() *cobra.Command {
 
 func doDelete(ctx context.Context, out io.Writer) error {
 	return withRunner(ctx, out, func(r runner.Runner, configs []util.VersionedConfig) error {
-		manifests, err := deployutil.GetManifestsFromHydrationDir(ctx, opts)
+		manifestListByConfig, err := r.Render(ctx, io.Discard, []graph.Artifact{}, false)
 		if err != nil {
-			return fmt.Errorf("getting manifests from hydration dir: %w", err)
+			return err
 		}
-		return r.Cleanup(ctx, out, dryRun, manifests)
+		return r.Cleanup(ctx, out, dryRun, manifestListByConfig)
 	})
 }
diff --git a/cmd/skaffold/app/cmd/dev.go b/cmd/skaffold/app/cmd/dev.go
index 5b41a986a..8c00af02a 100644
--- a/cmd/skaffold/app/cmd/dev.go
+++ b/cmd/skaffold/app/cmd/dev.go
@@ -24,7 +24,7 @@ import (
 
 	"github.com/spf13/cobra"
 
-	deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
@@ -74,11 +74,11 @@ func runDev(ctx context.Context, out io.Writer) error {
 
 				if r.HasDeployed() {
 					cleanup = func() {
-						manifests, err := deployutil.GetManifestsFromHydrationDir(ctx, opts)
+						manifestsByConfig, err := r.Render(ctx, io.Discard, []graph.Artifact{}, false)
 						if err != nil {
-							log.Entry(ctx).Warn(fmt.Errorf("getting manifests from hydration dir: %w", err))
+							log.Entry(ctx).Warn(fmt.Errorf("failed to render manifests: %w", err))
 						}
-						if err := r.Cleanup(context.Background(), out, false, manifests); err != nil {
+						if err := r.Cleanup(context.Background(), out, false, manifestsByConfig); err != nil {
 							log.Entry(ctx).Warn("deployer cleanup:", err)
 						}
 					}
diff --git a/examples/compose/Dockerfile b/examples/compose/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/compose/Dockerfile
+++ b/examples/compose/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/compose/go.mod b/examples/compose/go.mod
new file mode 100644
index 000000000..9023b37a8
--- /dev/null
+++ b/examples/compose/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/compose
+
+go 1.18
diff --git a/examples/cross-platform-builds/Dockerfile b/examples/cross-platform-builds/Dockerfile
index 0066fb077..326833f37 100644
--- a/examples/cross-platform-builds/Dockerfile
+++ b/examples/cross-platform-builds/Dockerfile
@@ -1,10 +1,12 @@
-FROM --platform=$BUILDPLATFORM golang:1.15 as builder
+FROM --platform=$BUILDPLATFORM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG TARGETOS
 ARG TARGETARCH
 ARG SKAFFOLD_GO_GCFLAGS
-RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/cross-platform-builds/go.mod b/examples/cross-platform-builds/go.mod
new file mode 100644
index 000000000..c4d9607a3
--- /dev/null
+++ b/examples/cross-platform-builds/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/cross-platform-builds
+
+go 1.18
diff --git a/examples/custom-buildx/Dockerfile b/examples/custom-buildx/Dockerfile
index d33b6af7c..dbe5a0f2a 100644
--- a/examples/custom-buildx/Dockerfile
+++ b/examples/custom-buildx/Dockerfile
@@ -5,13 +5,15 @@
 # TARGET{PLATFORM,OS,ARCH} are set to the desired platform
 FROM --platform=$BUILDPLATFORM golang:alpine AS builder
 
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
 ARG TARGETOS
 ARG TARGETARCH
-RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/docker-deploy/bert/Dockerfile b/examples/docker-deploy/bert/Dockerfile
index 19becf992..c01cebfca 100644
--- a/examples/docker-deploy/bert/Dockerfile
+++ b/examples/docker-deploy/bert/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3.10
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/docker-deploy/bert/go.mod b/examples/docker-deploy/bert/go.mod
new file mode 100644
index 000000000..61fcc27b5
--- /dev/null
+++ b/examples/docker-deploy/bert/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/docker-deploy/bert
+
+go 1.18
diff --git a/examples/docker-deploy/ernie/Dockerfile b/examples/docker-deploy/ernie/Dockerfile
index 19becf992..c01cebfca 100644
--- a/examples/docker-deploy/ernie/Dockerfile
+++ b/examples/docker-deploy/ernie/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3.10
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/docker-deploy/ernie/go.mod b/examples/docker-deploy/ernie/go.mod
new file mode 100644
index 000000000..2fb116270
--- /dev/null
+++ b/examples/docker-deploy/ernie/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/docker-deploy/ernie
+
+go 1.18
diff --git a/examples/gcb-kaniko/Dockerfile b/examples/gcb-kaniko/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/gcb-kaniko/Dockerfile
+++ b/examples/gcb-kaniko/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/gcb-kaniko/go.mod b/examples/gcb-kaniko/go.mod
new file mode 100644
index 000000000..66cb3926a
--- /dev/null
+++ b/examples/gcb-kaniko/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/gcb-kaniko
+
+go 1.18
diff --git a/examples/generate-pipeline/Dockerfile b/examples/generate-pipeline/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/generate-pipeline/Dockerfile
+++ b/examples/generate-pipeline/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/generate-pipeline/go.mod b/examples/generate-pipeline/go.mod
new file mode 100644
index 000000000..d78147d43
--- /dev/null
+++ b/examples/generate-pipeline/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/generate-pipeline
+
+go 1.18
diff --git a/examples/getting-started-kustomize/app/Dockerfile b/examples/getting-started-kustomize/app/Dockerfile
index 19becf992..c01cebfca 100644
--- a/examples/getting-started-kustomize/app/Dockerfile
+++ b/examples/getting-started-kustomize/app/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3.10
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/getting-started-kustomize/app/go.mod b/examples/getting-started-kustomize/app/go.mod
new file mode 100644
index 000000000..e361158c8
--- /dev/null
+++ b/examples/getting-started-kustomize/app/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/getting-started-kustomize
+
+go 1.18
diff --git a/examples/getting-started/Dockerfile b/examples/getting-started/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/getting-started/Dockerfile
+++ b/examples/getting-started/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/getting-started/go.mod b/examples/getting-started/go.mod
new file mode 100644
index 000000000..bc78b13a6
--- /dev/null
+++ b/examples/getting-started/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/getting-started
+
+go 1.18
diff --git a/examples/google-cloud-build/Dockerfile b/examples/google-cloud-build/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/google-cloud-build/Dockerfile
+++ b/examples/google-cloud-build/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/google-cloud-build/go.mod b/examples/google-cloud-build/go.mod
new file mode 100644
index 000000000..adee4fb0a
--- /dev/null
+++ b/examples/google-cloud-build/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/google-cloud-build
+
+go 1.18
diff --git a/examples/helm-deployment/Dockerfile b/examples/helm-deployment/Dockerfile
index 19becf992..c01cebfca 100644
--- a/examples/helm-deployment/Dockerfile
+++ b/examples/helm-deployment/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3.10
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/helm-deployment/go.mod b/examples/helm-deployment/go.mod
new file mode 100644
index 000000000..6d6d3ddc5
--- /dev/null
+++ b/examples/helm-deployment/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/helm-deployment
+
+go 1.18
diff --git a/examples/kaniko/Dockerfile b/examples/kaniko/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/kaniko/Dockerfile
+++ b/examples/kaniko/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/kaniko/go.mod b/examples/kaniko/go.mod
new file mode 100644
index 000000000..256a942c9
--- /dev/null
+++ b/examples/kaniko/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/kaniko
+
+go 1.18
diff --git a/examples/lifecycle-hooks/Dockerfile b/examples/lifecycle-hooks/Dockerfile
index 58c97be53..3b7221254 100644
--- a/examples/lifecycle-hooks/Dockerfile
+++ b/examples/lifecycle-hooks/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/lifecycle-hooks/go.mod b/examples/lifecycle-hooks/go.mod
new file mode 100644
index 000000000..d33ad1977
--- /dev/null
+++ b/examples/lifecycle-hooks/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/lifecycle-hooks
+
+go 1.18
diff --git a/examples/microservices/leeroy-app/Dockerfile b/examples/microservices/leeroy-app/Dockerfile
index 4403c2637..6d57aae25 100644
--- a/examples/microservices/leeroy-app/Dockerfile
+++ b/examples/microservices/leeroy-app/Dockerfile
@@ -1,9 +1,11 @@
 ARG BASE
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY app.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app .
 
 FROM $BASE
 COPY --from=builder /app .
diff --git a/examples/microservices/leeroy-app/go.mod b/examples/microservices/leeroy-app/go.mod
new file mode 100644
index 000000000..0805e1627
--- /dev/null
+++ b/examples/microservices/leeroy-app/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/microservices/leeroy-app
+
+go 1.18
diff --git a/examples/microservices/leeroy-web/Dockerfile b/examples/microservices/leeroy-web/Dockerfile
index 122977c1c..83e4a2e28 100644
--- a/examples/microservices/leeroy-web/Dockerfile
+++ b/examples/microservices/leeroy-web/Dockerfile
@@ -1,9 +1,11 @@
 ARG BASE
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY web.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app .
 
 FROM $BASE
 COPY --from=builder /app .
diff --git a/examples/microservices/leeroy-web/go.mod b/examples/microservices/leeroy-web/go.mod
new file mode 100644
index 000000000..cd5771064
--- /dev/null
+++ b/examples/microservices/leeroy-web/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/microservices/leeroy-web
+
+go 1.18
diff --git a/examples/multi-config-microservices/leeroy-app/Dockerfile b/examples/multi-config-microservices/leeroy-app/Dockerfile
index 4403c2637..6d57aae25 100644
--- a/examples/multi-config-microservices/leeroy-app/Dockerfile
+++ b/examples/multi-config-microservices/leeroy-app/Dockerfile
@@ -1,9 +1,11 @@
 ARG BASE
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY app.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app .
 
 FROM $BASE
 COPY --from=builder /app .
diff --git a/examples/multi-config-microservices/leeroy-app/go.mod b/examples/multi-config-microservices/leeroy-app/go.mod
new file mode 100644
index 000000000..f8b73a7f0
--- /dev/null
+++ b/examples/multi-config-microservices/leeroy-app/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/multi-config-microservices/leeroy-app
+
+go 1.18
diff --git a/examples/multi-config-microservices/leeroy-web/Dockerfile b/examples/multi-config-microservices/leeroy-web/Dockerfile
index 122977c1c..83e4a2e28 100644
--- a/examples/multi-config-microservices/leeroy-web/Dockerfile
+++ b/examples/multi-config-microservices/leeroy-web/Dockerfile
@@ -1,9 +1,11 @@
 ARG BASE
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY web.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app .
 
 FROM $BASE
 COPY --from=builder /app .
diff --git a/examples/multi-config-microservices/leeroy-web/go.mod b/examples/multi-config-microservices/leeroy-web/go.mod
new file mode 100644
index 000000000..6aa40ce02
--- /dev/null
+++ b/examples/multi-config-microservices/leeroy-web/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/multi-config-microservices/leeroy-web
+
+go 1.18
diff --git a/examples/profile-patches/base-service/Dockerfile b/examples/profile-patches/base-service/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/profile-patches/base-service/Dockerfile
+++ b/examples/profile-patches/base-service/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/profile-patches/base-service/go.mod b/examples/profile-patches/base-service/go.mod
new file mode 100644
index 000000000..ec9a5a76e
--- /dev/null
+++ b/examples/profile-patches/base-service/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/profile-patches/base-service
+
+go 1.18
diff --git a/examples/profile-patches/hello-service/Dockerfile b/examples/profile-patches/hello-service/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/profile-patches/hello-service/Dockerfile
+++ b/examples/profile-patches/hello-service/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/profile-patches/hello-service/go.mod b/examples/profile-patches/hello-service/go.mod
new file mode 100644
index 000000000..c8d36c082
--- /dev/null
+++ b/examples/profile-patches/hello-service/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/profile-patches/hello-service
+
+go 1.18
diff --git a/examples/profile-patches/world-service/Dockerfile b/examples/profile-patches/world-service/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/profile-patches/world-service/Dockerfile
+++ b/examples/profile-patches/world-service/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/profile-patches/world-service/go.mod b/examples/profile-patches/world-service/go.mod
new file mode 100644
index 000000000..b36efec6e
--- /dev/null
+++ b/examples/profile-patches/world-service/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/profile-patches/world-service
+
+go 1.18
diff --git a/examples/profiles/hello-service/Dockerfile b/examples/profiles/hello-service/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/profiles/hello-service/Dockerfile
+++ b/examples/profiles/hello-service/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/profiles/hello-service/go.mod b/examples/profiles/hello-service/go.mod
new file mode 100644
index 000000000..a98e5c4ba
--- /dev/null
+++ b/examples/profiles/hello-service/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/profiles/hello-service
+
+go 1.18
diff --git a/examples/profiles/world-service/Dockerfile b/examples/profiles/world-service/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/profiles/world-service/Dockerfile
+++ b/examples/profiles/world-service/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/profiles/world-service/go.mod b/examples/profiles/world-service/go.mod
new file mode 100644
index 000000000..e87efb5ec
--- /dev/null
+++ b/examples/profiles/world-service/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/profiles/world-service
+
+go 1.18
diff --git a/examples/simple-artifact-dependency/app/Dockerfile b/examples/simple-artifact-dependency/app/Dockerfile
index 237eace01..57dfed7ce 100644
--- a/examples/simple-artifact-dependency/app/Dockerfile
+++ b/examples/simple-artifact-dependency/app/Dockerfile
@@ -1,9 +1,11 @@
 ARG BASE
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app .
 
 FROM $BASE
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/simple-artifact-dependency/app/go.mod b/examples/simple-artifact-dependency/app/go.mod
new file mode 100644
index 000000000..e3cfc6875
--- /dev/null
+++ b/examples/simple-artifact-dependency/app/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/simple-artifact-dependency
+
+go 1.18
diff --git a/examples/tagging-with-environment-variables/Dockerfile b/examples/tagging-with-environment-variables/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/tagging-with-environment-variables/Dockerfile
+++ b/examples/tagging-with-environment-variables/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/tagging-with-environment-variables/go.mod b/examples/tagging-with-environment-variables/go.mod
new file mode 100644
index 000000000..bd6fb8b17
--- /dev/null
+++ b/examples/tagging-with-environment-variables/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/tagging-with-environment-variable
+
+go 1.18
diff --git a/pkg/skaffold/deploy/cloudrun/accessor.go b/pkg/skaffold/deploy/cloudrun/accessor.go
index a53911b3d..95a1878ca 100644
--- a/pkg/skaffold/deploy/cloudrun/accessor.go
+++ b/pkg/skaffold/deploy/cloudrun/accessor.go
@@ -19,6 +19,7 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"os"
 	"os/exec"
 	"strconv"
 
@@ -40,13 +41,14 @@ var (
 )
 
 type resourceTracker struct {
-	resources          []forwardedResource
+	resources          map[RunResourceName]*forwardedResource
 	configuredForwards []*latest.PortForwardResource
 	forwardedPorts     *util.PortSet
 }
 
 type forwardedResource struct {
 	name    RunResourceName
+	cmd     *exec.Cmd
 	cancel  context.CancelFunc
 	started bool
 	port    int
@@ -59,7 +61,7 @@ type forwarder interface {
 }
 
 // RunAccessor is an access.Accessor for Cloud Run resources
-// It uses `gcloud run proxy``to enable port forwarding for Cloud Run. This makes it easier to call IAM-protected Cloud Run services
+// It uses `gcloud run proxy“to enable port forwarding for Cloud Run. This makes it easier to call IAM-protected Cloud Run services
 // by going through localhost. In order to set up forwarding, the services must have their ingress setting set to "all", gcloud  must be
 // installed and on the path, and the currently configured gcloud user has run.services.invoke permission on the services being proxied
 type RunAccessor struct {
@@ -84,13 +86,21 @@ func NewAccessor(cfg Config, label string) *RunAccessor {
 
 // AddResource tracks an additional resource to port forward
 func (r *RunAccessor) AddResource(resource RunResourceName) {
+	if r.resources.resources == nil {
+		r.resources.resources = make(map[RunResourceName]*forwardedResource)
+	}
 	port := 0
 	for _, forward := range r.resources.configuredForwards {
 		if forward.Type == "service" && forward.Name == resource.Service {
 			port = forward.LocalPort
 		}
 	}
-	r.resources.resources = append(r.resources.resources, forwardedResource{name: resource, started: false, port: port})
+	if _, present := r.resources.resources[resource]; !present {
+		r.resources.resources[resource] = &forwardedResource{name: resource, started: false, port: port}
+	} else {
+		// signal that we need to start a new forward for this resource
+		r.resources.resources[resource].started = false
+	}
 }
 
 // Start begins port forwarding for the tracked Cloud Run services.
@@ -134,6 +144,10 @@ func (r *runProxyForwarder) Start(ctx context.Context, out io.Writer) error {
 		output.Red.Fprintln(out, "gcloud not found on path. Unable to set up Cloud Run port forwarding")
 		return sErrors.NewError(fmt.Errorf("gcloud not found"), &proto.ActionableErr{ErrCode: proto.StatusCode_PORT_FORWARD_RUN_GCLOUD_NOT_FOUND})
 	}
+	if r.resources.resources == nil {
+		// no forwards configured
+		return nil
+	}
 	for _, resource := range r.resources.resources {
 		if resource.port == 0 {
 			port := retrieveAvailablePort("localhost", 8080, r.resources.forwardedPorts)
@@ -155,7 +169,6 @@ func (r *runProxyForwarder) Start(ctx context.Context, out io.Writer) error {
 			go func() {
 				err := cmd.Wait()
 				if err != nil {
-					output.Red.Fprintf(out, "Port forward of %s quit unsuccessfuly: %v", resource.name.String(), err)
 					eventV2.TaskFailed(constants.PortForward, err)
 				} else {
 					eventV2.TaskSucceeded(constants.PortForward)
@@ -163,6 +176,7 @@ func (r *runProxyForwarder) Start(ctx context.Context, out io.Writer) error {
 			}()
 			eventV2.PortForwarded(int32(resource.port), schemautil.FromInt(443), "", "", resource.name.Project, "", "run-service", resource.name.Service, resource.name.String())
 			resource.started = true
+			resource.cmd = cmd
 		}
 	}
 	return nil
@@ -176,9 +190,17 @@ func getGcloudProxyArgs(resource RunResourceName, port int) []string {
 func (r *runProxyForwarder) Stop() {
 	for _, resource := range r.resources.resources {
 		if resource.cancel != nil {
-			resource.cancel()
+			if resource.cmd != nil {
+				if err := resource.cmd.Process.Signal(os.Interrupt); err != nil {
+					// signaling didn't work, force cancel
+					resource.cancel()
+				}
+			} else {
+				// we don't have a command, force cancel the context.
+				resource.cancel()
+			}
 			resource.cancel = nil
-			resource.started = false
+			resource.cmd = nil
 		}
 	}
 }
diff --git a/pkg/skaffold/deploy/cloudrun/deploy.go b/pkg/skaffold/deploy/cloudrun/deploy.go
index 2ca2ed399..949e09e28 100644
--- a/pkg/skaffold/deploy/cloudrun/deploy.go
+++ b/pkg/skaffold/deploy/cloudrun/deploy.go
@@ -101,8 +101,8 @@ func (d *Deployer) Dependencies() ([]string, error) {
 }
 
 // Cleanup deletes the created Cloud Run services
-func (d *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, manifests manifest.ManifestList) error {
-	return d.deleteRunService(ctx, out, dryRun, manifests)
+func (d *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, byConfig *manifest.ManifestListByConfig) error {
+	return d.deleteRunService(ctx, out, dryRun, byConfig.GetForConfig(d.configName))
 }
 
 // GetDebugger Get the Debugger for Cloud Run. Not supported by this deployer.
diff --git a/pkg/skaffold/deploy/deploy.go b/pkg/skaffold/deploy/deploy.go
index bae499f47..3456796e1 100644
--- a/pkg/skaffold/deploy/deploy.go
+++ b/pkg/skaffold/deploy/deploy.go
@@ -41,7 +41,7 @@ type Deployer interface {
 	Dependencies() ([]string, error)
 
 	// Cleanup deletes what was deployed by calling Deploy.
-	Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error
+	Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error
 
 	// GetDebugger returns a Deployer's implementation of a Debugger
 	GetDebugger() debug.Debugger
diff --git a/pkg/skaffold/deploy/deploy_mux.go b/pkg/skaffold/deploy/deploy_mux.go
index 13b19ea28..953314b07 100644
--- a/pkg/skaffold/deploy/deploy_mux.go
+++ b/pkg/skaffold/deploy/deploy_mux.go
@@ -160,13 +160,13 @@ func (m DeployerMux) Dependencies() ([]string, error) {
 	return deps.ToList(), nil
 }
 
-func (m DeployerMux) Cleanup(ctx context.Context, w io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (m DeployerMux) Cleanup(ctx context.Context, w io.Writer, dryRun bool, manifestsByConfig *manifest.ManifestListByConfig) error {
 	for _, deployer := range m.deployers {
 		ctx, endTrace := instrumentation.StartTrace(ctx, "Cleanup")
 		if dryRun {
 			output.Yellow.Fprintln(w, "Following resources would be deleted:")
 		}
-		if err := deployer.Cleanup(ctx, w, dryRun, list); err != nil {
+		if err := deployer.Cleanup(ctx, w, dryRun, manifestsByConfig); err != nil {
 			return err
 		}
 		endTrace()
diff --git a/pkg/skaffold/deploy/docker/deploy.go b/pkg/skaffold/deploy/docker/deploy.go
index b351448d1..1226eb069 100644
--- a/pkg/skaffold/deploy/docker/deploy.go
+++ b/pkg/skaffold/deploy/docker/deploy.go
@@ -295,7 +295,7 @@ func (d *Deployer) Dependencies() ([]string, error) {
 	return nil, nil
 }
 
-func (d *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (d *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ *manifest.ManifestListByConfig) error {
 	if dryRun {
 		for _, container := range d.tracker.DeployedContainers() {
 			output.Yellow.Fprintln(out, container.ID)
diff --git a/pkg/skaffold/deploy/helm/helm.go b/pkg/skaffold/deploy/helm/helm.go
index fa10f3600..9dbb6c0d4 100644
--- a/pkg/skaffold/deploy/helm/helm.go
+++ b/pkg/skaffold/deploy/helm/helm.go
@@ -355,7 +355,7 @@ func (h *Deployer) Dependencies() ([]string, error) {
 }
 
 // Cleanup deletes what was deployed by calling Deploy.
-func (h *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (h *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ *manifest.ManifestListByConfig) error {
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"DeployerType": "helm",
 	})
diff --git a/pkg/skaffold/deploy/kpt/kpt.go b/pkg/skaffold/deploy/kpt/kpt.go
index f2c9b0ed0..d0e50bc96 100644
--- a/pkg/skaffold/deploy/kpt/kpt.go
+++ b/pkg/skaffold/deploy/kpt/kpt.go
@@ -367,7 +367,7 @@ func (k *Deployer) Dependencies() ([]string, error) {
 }
 
 // Cleanup deletes what was deployed by calling `kpt live destroy`.
-func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ *manifest.ManifestListByConfig) error {
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"DeployerType": deployerName,
 	})
diff --git a/pkg/skaffold/deploy/kubectl/errors.go b/pkg/skaffold/deploy/kubectl/errors.go
index 45ce01a7a..5b3f35bbe 100644
--- a/pkg/skaffold/deploy/kubectl/errors.go
+++ b/pkg/skaffold/deploy/kubectl/errors.go
@@ -43,20 +43,6 @@ func versionGetErr(err error) error {
 		})
 }
 
-func offlineModeErr() error {
-	return sErrors.NewErrorWithStatusCode(
-		&proto.ActionableErr{
-			Message: "Cannot use offline mode if URL manifests are configured",
-			ErrCode: proto.StatusCode_DEPLOY_KUBECTL_OFFLINE_MODE_ERR,
-			Suggestions: []*proto.Suggestion{
-				{
-					SuggestionCode: proto.SuggestionCode_SET_RENDER_FLAG_OFFLINE_FALSE,
-					Action:         "Please rerun with --offline=false",
-				},
-			},
-		})
-}
-
 func waitForDeletionErr(err error) error {
 	return sErrors.NewError(err,
 		&proto.ActionableErr{
diff --git a/pkg/skaffold/deploy/kubectl/kubectl.go b/pkg/skaffold/deploy/kubectl/kubectl.go
index fd4f44c25..96de62064 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl.go
@@ -21,7 +21,6 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"os"
 	"strings"
 
 	"github.com/segmentio/textio"
@@ -328,50 +327,6 @@ func (k *Deployer) manifestFiles(manifests []string) ([]string, error) {
 	return filteredManifests, nil
 }
 
-// readManifests reads the manifests to deploy/delete.
-func (k *Deployer) readManifests(ctx context.Context, offline bool) (manifest.ManifestList, error) {
-	var manifests []string
-
-	// Clean the temporary directory that holds the manifests downloaded from GCS
-	defer os.RemoveAll(k.gcsManifestDir)
-
-	// Append URL manifests. URL manifests are excluded from `Dependencies`.
-	hasURLManifest := false
-	for _, manifest := range k.KubectlDeploy.Manifests {
-		if util.IsURL(manifest) {
-			manifests = append(manifests, manifest)
-			hasURLManifest = true
-		}
-	}
-
-	if len(manifests) == 0 {
-		return manifest.ManifestList{}, nil
-	}
-
-	if !offline {
-		return k.kubectl.ReadManifests(ctx, manifests)
-	}
-
-	// In case no URLs are provided, we can stay offline - no need to run "kubectl create" which
-	// would try to connect to a cluster (https://github.com/kubernetes/kubernetes/issues/51475)
-	if hasURLManifest {
-		return nil, offlineModeErr()
-	}
-	return createManifestList(manifests)
-}
-
-func createManifestList(manifests []string) (manifest.ManifestList, error) {
-	var manifestList manifest.ManifestList
-	for _, manifestFilePath := range manifests {
-		manifestFileContent, err := os.ReadFile(manifestFilePath)
-		if err != nil {
-			return nil, readManifestErr(fmt.Errorf("reading manifest file %v: %w", manifestFilePath, err))
-		}
-		manifestList.Append(manifestFileContent)
-	}
-	return manifestList, nil
-}
-
 // readRemoteManifests will try to read manifests from the given kubernetes
 // context in the specified namespace and for the specified type
 func (k *Deployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) {
@@ -393,15 +348,14 @@ func (k *Deployer) readRemoteManifest(ctx context.Context, name string) ([]byte,
 }
 
 // Cleanup deletes what was deployed by calling Deploy.
-func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, manifests manifest.ManifestList) error {
+func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, manifestsByConfig *manifest.ManifestListByConfig) error {
+	var manifests manifest.ManifestList
+	if manifestsByConfig != nil {
+		manifests = append(manifests, manifestsByConfig.GetForConfig(k.ConfigName())...)
+	}
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"DeployerType": "kubectl",
 	})
-	other, err := k.readManifests(ctx, false)
-	manifests = append(manifests, other...)
-	if err != nil {
-		return err
-	}
 	if dryRun {
 		for _, manifest := range manifests {
 			output.White.Fprintf(out, "---\n%s", manifest)
@@ -411,6 +365,7 @@ func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, mani
 	// revert remote manifests
 	// TODO(dgageot): That seems super dangerous and I don't understand
 	// why we need to update resources just before we delete them.
+	// todo clean up this block as RemoteManifests is no longer used for deployer
 	if len(k.RemoteManifests) > 0 {
 		var rm manifest.ManifestList
 		for _, m := range k.RemoteManifests {
diff --git a/pkg/skaffold/deploy/kustomize/kustomize.go b/pkg/skaffold/deploy/kustomize/kustomize.go
index 2f147a04f..d002cde0b 100644
--- a/pkg/skaffold/deploy/kustomize/kustomize.go
+++ b/pkg/skaffold/deploy/kustomize/kustomize.go
@@ -369,7 +369,7 @@ func (k *Deployer) renderManifests(ctx context.Context, out io.Writer, builds []
 }
 
 // Cleanup deletes what was deployed by calling Deploy.
-func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (k *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ *manifest.ManifestListByConfig) error {
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"DeployerType": "kustomize",
 	})
diff --git a/pkg/skaffold/runner/runner.go b/pkg/skaffold/runner/runner.go
index e37ddba28..56053140f 100644
--- a/pkg/skaffold/runner/runner.go
+++ b/pkg/skaffold/runner/runner.go
@@ -35,7 +35,7 @@ type Runner interface {
 	Apply(context.Context, io.Writer) error
 	ApplyDefaultRepo(tag string) (string, error)
 	Build(context.Context, io.Writer, []*latest.Artifact) ([]graph.Artifact, error)
-	Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error
+	Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error
 	Dev(context.Context, io.Writer, []*latest.Artifact) error
 	// Deploy and DeployAndLog: Do they need the `graph.Artifact` and could use render output.
 	Deploy(context.Context, io.Writer, []graph.Artifact, *manifest.ManifestListByConfig) error
diff --git a/pkg/skaffold/runner/timings.go b/pkg/skaffold/runner/timings.go
index f2a660f1b..6d2be1104 100644
--- a/pkg/skaffold/runner/timings.go
+++ b/pkg/skaffold/runner/timings.go
@@ -107,11 +107,11 @@ func (w withTimings) Deploy(ctx context.Context, out io.Writer, builds []graph.A
 	return err
 }
 
-func (w withTimings) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (w withTimings) Cleanup(ctx context.Context, out io.Writer, dryRun bool, config *manifest.ManifestListByConfig) error {
 	start := time.Now()
 	output.Default.Fprintln(out, "Cleaning up...")
 
-	err := w.Deployer.Cleanup(ctx, out, dryRun, list)
+	err := w.Deployer.Cleanup(ctx, out, dryRun, config)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/skaffold/runner/v2/cleanup.go b/pkg/skaffold/runner/v2/cleanup.go
index 729215738..1fb93e7ec 100644
--- a/pkg/skaffold/runner/v2/cleanup.go
+++ b/pkg/skaffold/runner/v2/cleanup.go
@@ -22,6 +22,6 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 )
 
-func (r *SkaffoldRunner) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
-	return r.deployer.Cleanup(ctx, out, dryRun, list)
+func (r *SkaffoldRunner) Cleanup(ctx context.Context, out io.Writer, dryRun bool, manifestListByConfig *manifest.ManifestListByConfig) error {
+	return r.deployer.Cleanup(ctx, out, dryRun, manifestListByConfig)
 }


Test patch:
diff --git a/cmd/skaffold/app/cmd/dev_test.go b/cmd/skaffold/app/cmd/dev_test.go
index 6ab37b8ca..ff5d3347b 100644
--- a/cmd/skaffold/app/cmd/dev_test.go
+++ b/cmd/skaffold/app/cmd/dev_test.go
@@ -22,6 +22,7 @@ import (
 	"testing"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
 	runcontext "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext/v2"
@@ -32,10 +33,11 @@ import (
 
 type mockDevRunner struct {
 	runner.Runner
-	hasBuilt    bool
-	hasDeployed bool
-	errDev      error
-	calls       []string
+	hasBuilt          bool
+	hasDeployed       bool
+	errDev            error
+	calls             []string
+	manifestsByConfig *manifest.ManifestListByConfig
 }
 
 func (r *mockDevRunner) Dev(context.Context, io.Writer, []*latest.Artifact) error {
@@ -58,11 +60,16 @@ func (r *mockDevRunner) Prune(context.Context, io.Writer) error {
 	return nil
 }
 
-func (r *mockDevRunner) Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error {
+func (r *mockDevRunner) Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error {
 	r.calls = append(r.calls, "Cleanup")
 	return nil
 }
 
+func (r *mockDevRunner) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool) (*manifest.ManifestListByConfig, error) {
+	r.calls = append(r.calls, "Render")
+	return r.manifestsByConfig, nil
+}
+
 func TestDoDev(t *testing.T) {
 	tests := []struct {
 		description   string
@@ -74,7 +81,7 @@ func TestDoDev(t *testing.T) {
 			description:   "cleanup and then prune",
 			hasBuilt:      true,
 			hasDeployed:   true,
-			expectedCalls: []string{"Dev", "HasDeployed", "HasBuilt", "Cleanup", "Prune"},
+			expectedCalls: []string{"Dev", "HasDeployed", "HasBuilt", "Render", "Cleanup", "Prune"},
 		},
 		{
 			description:   "hasn't deployed",
@@ -139,7 +146,11 @@ func (m *mockConfigChangeRunner) Prune(context.Context, io.Writer) error {
 	return nil
 }
 
-func (m *mockConfigChangeRunner) Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error {
+func (m *mockConfigChangeRunner) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool) (*manifest.ManifestListByConfig, error) {
+	return nil, nil
+}
+
+func (m *mockConfigChangeRunner) Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error {
 	return nil
 }
 
diff --git a/examples/custom-tests/Dockerfile b/examples/custom-tests/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/custom-tests/Dockerfile
+++ b/examples/custom-tests/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/custom-tests/go.mod b/examples/custom-tests/go.mod
new file mode 100644
index 000000000..403412976
--- /dev/null
+++ b/examples/custom-tests/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/custom-tests
+
+go 1.18
diff --git a/examples/structure-tests/Dockerfile b/examples/structure-tests/Dockerfile
index 415a8456f..67f8c3cbb 100644
--- a/examples/structure-tests/Dockerfile
+++ b/examples/structure-tests/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM golang:1.18 as builder
+WORKDIR /code
 COPY main.go .
+COPY go.mod .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/examples/structure-tests/go.mod b/examples/structure-tests/go.mod
new file mode 100644
index 000000000..3348044c6
--- /dev/null
+++ b/examples/structure-tests/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/structure-tests
+
+go 1.18
diff --git a/integration/delete_test.go b/integration/delete_test.go
new file mode 100644
index 000000000..e37004b7b
--- /dev/null
+++ b/integration/delete_test.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 integration
+
+import (
+	"testing"
+	"time"
+
+	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
+)
+
+func TestDelete(t *testing.T) {
+	var tests = []struct {
+		description string
+		dir         string
+		args        []string
+		pods        []string
+		deployments []string
+		env         []string
+	}{
+		{
+			description: "getting-started",
+			dir:         "testdata/getting-started",
+			pods:        []string{"getting-started"},
+		},
+		{
+			description: "microservices",
+			dir:         "examples/microservices",
+			args:        []string{"--status-check=false"},
+			deployments: []string{"leeroy-app", "leeroy-web"},
+		},
+		{
+			description: "multi-config-microservices",
+			dir:         "examples/multi-config-microservices",
+			deployments: []string{"leeroy-app", "leeroy-web"},
+		},
+		{
+			description: "multiple deployers",
+			dir:         "testdata/deploy-multiple",
+			pods:        []string{"deploy-kubectl", "deploy-kustomize"},
+		},
+	}
+
+	MarkIntegrationTest(t, CanRunWithoutGcp)
+
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			ns, client := SetupNamespace(t)
+
+			args := append(test.args, "--cache-artifacts=false")
+			skaffold.Run(args...).InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunOrFail(t)
+
+			client.WaitForPodsReady(test.pods...)
+			client.waitForDeploymentsToStabilizeWithTimeout(time.Minute*2, test.deployments...)
+
+			skaffold.Delete().InDir(test.dir).InNs(ns.Name).WithEnv(test.env).RunOrFail(t)
+		})
+	}
+}
diff --git a/pkg/skaffold/deploy/cloudrun/accessor_test.go b/pkg/skaffold/deploy/cloudrun/accessor_test.go
index 34e2a82ec..cf39ea936 100644
--- a/pkg/skaffold/deploy/cloudrun/accessor_test.go
+++ b/pkg/skaffold/deploy/cloudrun/accessor_test.go
@@ -154,6 +154,32 @@ func TestResourcesAddedConfigurePorts(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "resources added twice only have one port forward configured",
+			resources: []RunResourceName{
+				{
+					Project: "test-proj",
+					Region:  "test-region",
+					Service: "test-service",
+				},
+				{
+					Project: "test-proj",
+					Region:  "test-region",
+					Service: "test-service",
+				},
+			},
+			forwardConfigs: []*latest.PortForwardResource{},
+			outputs: []forwardedResource{
+				{
+					name: RunResourceName{
+						Project: "test-proj",
+						Region:  "test-region",
+						Service: "test-service",
+					},
+					port: 0,
+				},
+			},
+		},
 	}
 
 	for _, test := range tests {
@@ -167,8 +193,11 @@ func TestResourcesAddedConfigurePorts(t *testing.T) {
 			if len(test.outputs) != len(accessor.resources.resources) {
 				t.Fatalf("Mismatch in expected outputs. Expected %v, got %v", test.outputs, accessor.resources.resources)
 			}
-			for i, output := range test.outputs {
-				got := accessor.resources.resources[i]
+			for _, output := range test.outputs {
+				got, found := accessor.resources.resources[output.name]
+				if !found {
+					t.Fatalf("expected to find port forward for %v but got nothing", output.name)
+				}
 				if output.name != got.name || output.port != got.port {
 					t.Fatalf("did not get expected port set. Expected %v, got %v", output, got)
 				}
diff --git a/pkg/skaffold/deploy/cloudrun/deploy_test.go b/pkg/skaffold/deploy/cloudrun/deploy_test.go
index c0b55254c..382ba5292 100644
--- a/pkg/skaffold/deploy/cloudrun/deploy_test.go
+++ b/pkg/skaffold/deploy/cloudrun/deploy_test.go
@@ -327,9 +327,11 @@ func TestCleanup(tOuter *testing.T) {
 			deployer, _ := NewDeployer(&runcontext.RunContext{}, &label.DefaultLabeller{}, &latest.CloudRunDeploy{ProjectID: test.defaultProject, Region: test.region}, configName)
 			deployer.clientOptions = append(deployer.clientOptions, option.WithEndpoint(ts.URL), option.WithoutAuthentication())
 			deployer.useGcpOptions = false
+			manifestListByConfig := manifest.NewManifestListByConfig()
 			manifest, _ := json.Marshal(test.toDelete)
 			manifests := [][]byte{manifest}
-			err := deployer.Cleanup(context.Background(), os.Stderr, false, manifests)
+			manifestListByConfig.Add(configName, manifests)
+			err := deployer.Cleanup(context.Background(), os.Stderr, false, &manifestListByConfig)
 			if test.httpErr == 0 && err != nil {
 				t.Fatalf("Expected success but got err: %v", err)
 			} else if test.httpErr != 0 && err == nil {
diff --git a/pkg/skaffold/deploy/deploy_mux_test.go b/pkg/skaffold/deploy/deploy_mux_test.go
index f6a4212f5..9f9fc6796 100644
--- a/pkg/skaffold/deploy/deploy_mux_test.go
+++ b/pkg/skaffold/deploy/deploy_mux_test.go
@@ -87,7 +87,7 @@ func (m *MockDeployer) Dependencies() ([]string, error) {
 	return m.dependencies, m.dependenciesErr
 }
 
-func (m *MockDeployer) Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error {
+func (m *MockDeployer) Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error {
 	return m.cleanupErr
 }
 
diff --git a/pkg/skaffold/deploy/kubectl/kubectl_test.go b/pkg/skaffold/deploy/kubectl/kubectl_test.go
index b8a935441..a9edb4d47 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl_test.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl_test.go
@@ -337,7 +337,7 @@ func TestKubectlCleanup(t *testing.T) {
 				true)
 			t.CheckNoError(errR)
 
-			err = k.Cleanup(context.Background(), io.Discard, test.dryRun, m.GetForConfig(configName))
+			err = k.Cleanup(context.Background(), io.Discard, test.dryRun, m)
 
 			t.CheckError(test.shouldErr, err)
 		})
diff --git a/pkg/skaffold/render/generate/generate_test.go b/pkg/skaffold/render/generate/generate_test.go
index f15e39b2a..d638b6e5d 100644
--- a/pkg/skaffold/render/generate/generate_test.go
+++ b/pkg/skaffold/render/generate/generate_test.go
@@ -183,7 +183,7 @@ func TestGenerate(t *testing.T) {
 
 func TestGenerateFromURLManifest(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintln(w, podYaml)
+		fmt.Fprint(w, podYaml)
 	}))
 
 	defer ts.Close()
diff --git a/pkg/skaffold/runner/timings_test.go b/pkg/skaffold/runner/timings_test.go
index 7553022e1..43ad2e134 100644
--- a/pkg/skaffold/runner/timings_test.go
+++ b/pkg/skaffold/runner/timings_test.go
@@ -100,7 +100,7 @@ func (m *mockDeployer) Deploy(context.Context, io.Writer, []graph.Artifact, *man
 	return nil
 }
 
-func (m *mockDeployer) Cleanup(context.Context, io.Writer, bool, manifest.ManifestList) error {
+func (m *mockDeployer) Cleanup(context.Context, io.Writer, bool, *manifest.ManifestListByConfig) error {
 	if m.err {
 		return errors.New("Unable to cleanup")
 	}
diff --git a/pkg/skaffold/runner/v2/runner_test.go b/pkg/skaffold/runner/v2/runner_test.go
index 5c16cc9a4..07feabdce 100644
--- a/pkg/skaffold/runner/v2/runner_test.go
+++ b/pkg/skaffold/runner/v2/runner_test.go
@@ -143,7 +143,7 @@ func (t *TestBench) TestDependencies(context.Context, *latest.Artifact) ([]strin
 	return nil, nil
 }
 func (t *TestBench) Dependencies() ([]string, error) { return nil, nil }
-func (t *TestBench) Cleanup(ctx context.Context, out io.Writer, dryRun bool, list manifest.ManifestList) error {
+func (t *TestBench) Cleanup(ctx context.Context, out io.Writer, dryRun bool, byConfig *manifest.ManifestListByConfig) error {
 	return nil
 }
 func (t *TestBench) Prune(ctx context.Context, out io.Writer) error { return nil }