diff --git a/.drone/drone.yml b/.drone/drone.yml
deleted file mode 100644
index a8c2778..0000000
--- a/.drone/drone.yml
+++ /dev/null
@@ -1,180 +0,0 @@
-kind: pipeline
-type: docker
-name: linux-amd64
-
-platform:
-  arch: amd64
-  os: linux
-
-steps:
-- name: test
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - make bootstrap test test-cover
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY: ${DEV_REGISTRY:-docker.io}
-    DRYCC_REGISTRY: ${DRYCC_REGISTRY:-docker.io}
-  when:
-    event:
-    - push
-    - tag
-    - pull_request
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-- name: codecov
-  image: docker.io/drycc/go-dev
-  pull: always
-  commands:
-    - curl -s https://codecov.io/bash | bash
-  environment:
-    CODECOV_TOKEN:
-      from_secret: codecov_token
-  when:
-    status:
-    - success
-
-- name: publish
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    DOCKER_USERNAME:
-      from_secret: docker_username
-    DOCKER_PASSWORD:
-      from_secret: docker_password
-  when:
-    event:
-    - push
-    - tag
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-trigger:
-  event:
-  - push
-  - tag
-  - pull_request
-
-volumes:
-  - name: image_registries
-    host:
-      path: /etc/containers/registries.conf
-
----
-kind: pipeline
-type: docker
-name: linux-arm64
-
-platform:
-  arch: arm64
-  os: linux
-
-steps:
-- name: publish
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-arm64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    DOCKER_USERNAME:
-      from_secret: docker_username
-    DOCKER_PASSWORD:
-      from_secret: docker_password
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-trigger:
-  event:
-  - push
-  - tag
-
-volumes:
-- name: image_registries
-  host:
-    path: /etc/containers/registries.conf
-
----
-kind: pipeline
-type: docker
-name: manifest
-
-steps:
-- name: generate manifest
-  image: docker.io/drycc/go-dev
-  pull: always
-  commands:
-  - sed -i "s/docker.io/$${DRYCC_REGISTRY}/g" .drone/manifest.tmpl
-  environment:
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-- name: publish
-  image: plugins/manifest
-  settings:
-    spec: .drone/manifest.tmpl
-    username:
-      from_secret: docker_username
-    password:
-      from_secret: docker_password
-  environment:
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-trigger:
-  event:
-  - push
-  - tag
-
-depends_on:
-- linux-amd64
-- linux-arm64
-
----
-kind: pipeline
-type: docker
-name: chart
-
-steps:
-- name: generate chart
-  image: docker.io/drycc/python-dev
-  commands:
-  - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && echo \"${DRONE_TAG:1}\" || echo \"canary\")
-  - sed -i "s/image_tag:\ \"canary\"/image_tag:\ $IMAGE_TAG/g" charts/builder/values.yaml
-  - helm package charts/builder --version ${DRONE_TAG:-v1.0.0}
-  - curl -u $CHARTMUSEUM_USERNAME:$CHARTMUSEUM_PASSWORD -F chart=@builder-${DRONE_TAG:-v1.0.0}.tgz "$CHARTMUSEUM_API/api/$([ -z $DRONE_TAG ] && echo testing || echo stable)/charts"
-  environment:
-    CHARTMUSEUM_USERNAME:
-      from_secret: chartmuseum_username
-    CHARTMUSEUM_PASSWORD:
-      from_secret: chartmuseum_password
-    CHARTMUSEUM_API:
-      from_secret: chartmuseum_api
-
-trigger:
-  event:
-  - push
-  - tag
diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl
deleted file mode 100644
index 00b2156..0000000
--- a/.drone/manifest.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-image: docker.io/drycc/builder:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
-{{#if build.tags}}
-tags:
-{{#each build.tags}}
-  - {{this}}
-{{/each}}
-{{/if}}
-manifests:
-  -
-    image: docker.io/drycc/builder:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
-    platform:
-      architecture: amd64
-      os: linux
-  -
-    image: docker.io/drycc/builder:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
-    platform:
-      architecture: arm64
-      os: linux
diff --git a/.gitignore b/.gitignore
index 1011d1f..aeac0f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@ rootfs/usr/bin/
 coverage.txt
 testdata/hooks/pre-receive
 .idea/
-
+.vscode/
diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml
new file mode 100644
index 0000000..22eba83
--- /dev/null
+++ b/.woodpecker/build-linux.yml
@@ -0,0 +1,35 @@
+matrix:
+  platform:
+    - linux/amd64
+    - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: publish-linux
+  image: bash
+  commands:
+  - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM)
+  - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1
+  - make podman-build podman-immutable-push
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- test-linux
diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml
new file mode 100644
index 0000000..97fc599
--- /dev/null
+++ b/.woodpecker/chart.yaml
@@ -0,0 +1,34 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-chart
+  image: bash
+  commands:
+  - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG)
+  - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\")
+  - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION)
+  - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION)
+  - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml
+  - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION
+  - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin
+  - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts)
+  environment:
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- manifest
+
diff --git a/.woodpecker/manifest.tmpl b/.woodpecker/manifest.tmpl
new file mode 100644
index 0000000..8739535
--- /dev/null
+++ b/.woodpecker/manifest.tmpl
@@ -0,0 +1,18 @@
+image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
+{{#if build.tags}}
+tags:
+{{#each build.tags}}
+  - {{this}}
+{{/each}}
+{{/if}}
+manifests:
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
+    platform:
+      architecture: amd64
+      os: linux
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
+    platform:
+      architecture: arm64
+      os: linux
diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml
new file mode 100644
index 0000000..19fc70c
--- /dev/null
+++ b/.woodpecker/manifest.yml
@@ -0,0 +1,43 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-manifest
+  image: bash
+  commands:
+  - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl
+  - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl
+  environment:
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+- name: publish-manifest
+  image: bash
+  commands:
+  - podman run --rm
+    -e PLUGIN_SPEC=.woodpecker/manifest.tmpl
+    -e PLUGIN_USERNAME=$CONTAINER_USERNAME
+    -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD
+    -e DRONE_TAG=$CI_COMMIT_TAG
+    -v $(pwd):$(pwd)
+    -w $(pwd)
+    docker.io/plugins/manifest
+  environment:
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+depends_on:
+- build-linux
diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml
new file mode 100644
index 0000000..f635d8c
--- /dev/null
+++ b/.woodpecker/test-linux.yml
@@ -0,0 +1,26 @@
+matrix:
+  platform:
+  - linux/amd64
+  - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: test-linux
+  image: bash
+  commands:
+  - make bootstrap test test-cover
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    CODECOV_TOKEN:
+      from_secret: codecov_token
+  when:
+    event:
+    - push
+    - tag
+    - cron
diff --git a/Dockerfile b/Dockerfile
index f188e85..b6688cc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,36 +1,39 @@
-FROM docker.io/minio/mc:latest as mc
-
-
-FROM docker.io/drycc/go-dev:latest AS build
 ARG LDFLAGS
-ADD . /app
+ARG CODENAME
+
+FROM registry.drycc.cc/drycc/go-dev:latest AS build
+ADD . /workspace
 RUN export GO111MODULE=on \
-  && cd /app \
-  && CGO_ENABLED=0 go build -ldflags "${LDFLAGS}" -o /usr/local/bin/boot boot.go
+  && cd /workspace \
+  && CGO_ENABLED=0 init-stack go build -ldflags "${LDFLAGS}" -o /usr/local/bin/boot boot.go
 
+FROM registry.drycc.cc/drycc/base:${CODENAME}
 
-FROM docker.io/library/alpine:3.12
+ARG DRYCC_UID=1001 \
+  DRYCC_GID=1001 \
+  DRYCC_HOME_DIR=/workspace \
+  RCLONE_VERSION="1.71.1" \
+  JQ_VERSION="1.7.1"
 
-RUN adduser \
-	-s /bin/sh \
-	-D \
-	-h /home/git \
-	git \
-	git
+RUN groupadd drycc --gid ${DRYCC_GID} \
+  && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR}
 
-COPY rootfs /
-COPY --from=mc /usr/bin/mc /bin/mc
+COPY rootfs/bin /bin/
+COPY rootfs/etc/ssh /etc/ssh/
+COPY rootfs/container-entrypoint.sh /container-entrypoint.sh
 COPY --from=build /usr/local/bin/boot /usr/bin/boot
 
-RUN apk add --update git sudo openssh-server coreutils tar xz jq bash \
+RUN install-packages git openssh-server coreutils xz-utils tar \
+  && install-stack rclone $RCLONE_VERSION \
+  && install-stack jq $JQ_VERSION \
   && mkdir -p /var/run/sshd \
   && rm -rf /etc/ssh/ssh_host* \
-  && mkdir /apps \
-  && passwd -u git \
-  && chmod +x /bin/create_bucket /bin/normalize_storage /docker-entrypoint.sh
+  && chmod +x /bin/create_bucket /container-entrypoint.sh
 
-ENTRYPOINT ["/docker-entrypoint.sh"]
+USER ${DRYCC_UID}
+WORKDIR ${DRYCC_HOME_DIR}
 
+ENTRYPOINT ["init-stack", "/container-entrypoint.sh"]
 CMD ["/usr/bin/boot", "server"]
 
 EXPOSE 2223
diff --git a/Makefile b/Makefile
index a3d9a5c..9c1e0cc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,31 +1,30 @@
 SHORT_NAME ?= builder
+DRYCC_REGISTRY ?= ${DEV_REGISTRY}
 
 include versioning.mk
 
-# dockerized development environment variables
+# container development environment variables
 REPO_PATH := github.com/drycc/${SHORT_NAME}
 DEV_ENV_IMAGE := ${DEV_REGISTRY}/drycc/go-dev
-DEV_ENV_WORK_DIR := /go/src/${REPO_PATH}
-DEV_ENV_PREFIX := docker run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR}
+DEV_ENV_WORK_DIR := /root/go/src/${REPO_PATH}
+DEV_ENV_PREFIX := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} -e CODECOV_TOKEN=${CODECOV_TOKEN} --entrypoint testdata/bin/fake-k8s
 DEV_ENV_CMD := ${DEV_ENV_PREFIX} ${DEV_ENV_IMAGE}
 PLATFORM ?= linux/amd64,linux/arm64
 
-# SemVer with build information is defined in the SemVer 2 spec, but Docker
+# SemVer with build information is defined in the SemVer 2 spec, but podman
 # doesn't allow +, so we use -.
 BINARY_DEST_DIR := rootfs/usr/bin
 # Common flags passed into Go's linker.
 LDFLAGS := "-s -w -X main.version=${VERSION}"
-# Docker Root FS
+# Container Root FS
 BINDIR := ./rootfs
 
-DRYCC_REGISTRY ?= ${DEV_REGISTRY}
-
 bootstrap:
 	${DEV_ENV_CMD} go mod vendor
 
-# This illustrates a two-stage Docker build. docker-compile runs inside of
-# the Docker environment. Other alternatives are cross-compiling, doing
-# the build as a `docker build`.
+# This illustrates a two-stage Podman build. podman-compile runs inside of
+# the container environment. Other alternatives are cross-compiling, doing
+# the build as a `podman build`.
 build-binary:
 	CGO_ENABLED=0 go build -ldflags ${LDFLAGS} -o ${BINARY_DEST_DIR}/boot boot.go
 
@@ -35,20 +34,17 @@ build:
 test: test-style test-unit
 
 test-style:
-	${DEV_ENV_CMD} lint --deadline
+	${DEV_ENV_CMD} lint
 
 test-unit:
-	${DEV_ENV_CMD} sh -c 'go test --race ./...'
+	${DEV_ENV_CMD} go test --race ./...
 
 test-cover:
 	${DEV_ENV_CMD} test-cover.sh
 
-docker-build:
-	docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} .
-	docker tag ${IMAGE} ${MUTABLE_IMAGE}
-
-docker-buildx:
-	docker buildx build --platform ${PLATFORM} -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} . --push
+podman-build:
+	podman build -t ${IMAGE} --build-arg LDFLAGS=${LDFLAGS} --build-arg CODENAME=${CODENAME} .
+	podman tag ${IMAGE} ${MUTABLE_IMAGE}
 
 check-kubectl:
 	@if [ -z $$(which kubectl) ]; then \
@@ -56,7 +52,7 @@ check-kubectl:
 		exit 2; \
 	fi
 
-deploy: check-kubectl docker-build docker-push
+deploy: check-kubectl podman-build podman-push
 	kubectl --namespace=drycc patch deployment drycc-$(SHORT_NAME) --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"$(IMAGE)"}]'
 
-.PHONY: bootstrap depup build docker-build test test-style test-unit test-cover deploy
+.PHONY: bootstrap depup build podman-build test test-style test-unit test-cover deploy
diff --git a/README.md b/README.md
index 43a6f07..d1e821f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
-
 # Drycc Builder v2
 
-[![Build Status](https://drone.drycc.cc/api/badges/drycc/builder/status.svg)](https://drone.drycc.cc/drycc/builder)
+[![Build Status](https://woodpecker.drycc.cc/api/badges/drycc/builder/status.svg)](https://woodpecker.drycc.cc/drycc/builder)
 [![codecov](https://codecov.io/gh/drycc/builder/branch/main/graph/badge.svg)](https://codecov.io/gh/drycc/builder)
 [![Go Report Card](https://goreportcard.com/badge/github.com/drycc/builder)](https://goreportcard.com/report/github.com/drycc/builder)
 [![codebeat badge](https://codebeat.co/badges/0507e5d5-163b-4280-84ea-83bd2e0c8e41)](https://codebeat.co/projects/github-com-drycc-builder-main)
@@ -19,10 +18,7 @@ We welcome your input! If you have feedback, please [submit an issue][issues]. I
 The builder is primarily a git server that responds to `git push`es by executing either the `git-receive-pack` or `git-upload-pack` hook. After it executes one of those hooks, it takes the following high level steps in order:
 
 1. Calls `git archive` to produce a tarball (i.e. a `.tar.gz` file) on the local file system
-2. Saves the tarball to centralized object storage according to the following rules:
-	- If the `BUILDER_STORAGE` environment variable is other than `minio`, attempts to create the appropriate storage driver and saves using this driver.
-  - Otherwise, if `BUILDER_STORAGE` is `minio` and the `DRYCC_MINIO_SERVICE_HOST` and `DRYCC_MINIO_SERVICE_PORT` environment variables exist (these are standard [Kubernetes service discovery environment variables](http://kubernetes.io/docs/user-guide/services/#environment-variables)), saves to the [S3 API][s3-api-ref] compatible server at `http://$DRYCC_MINIO_SERVICE_HOST:$DRYCC_MINIO_SERVICE_HOST`
-3. Starts a new [Kubernetes Pod](http://kubernetes.io/docs/user-guide/pods/) to build the code, according to the following rules:
+2. Starts a new [Kubernetes Pod](http://kubernetes.io/docs/user-guide/pods/) to build the code, according to the following rules:
   - If there is a `Dockerfile`, build the container with imagebuilder
   - Otherwise, use imagebuilder to build CNCF native buildpack
   - You can use `DRYCC_STACK` specifies the build type. Currently, it supports two types: `buildpack` and `container`
@@ -35,6 +31,7 @@ Builder currently supports the following off-cluster storage backends:
 * AWS/S3
 * Azure
 * Swift
+* Alibaba OSS
 
 # Development
 
@@ -46,24 +43,16 @@ The Drycc project welcomes contributions from all developers. The high level pro
 	* If your PR fixes any [issues][issues], make sure you write `Fixes #1234` in your PR description (where `#1234` is the number of the issue you're closing)
 * The Drycc core contributors will review your code. After each of them sign off on your code, they'll label your PR with `LGTM1` and `LGTM2` (respectively). Once that happens, a contributor will merge it
 
-## Docker Based Development Environment
+## Container Based Development Environment
 
-The preferred environment for development uses [the `go-dev` Docker image](https://github.com/drycc/docker-go-dev). The tools described in this section are used to build, test, package and release each version of Drycc.
+The preferred environment for development uses [the `go-dev` Container image](https://github.com/drycc/go-dev). The tools described in this section are used to build, test, package and release each version of Drycc.
 
-To use it yourself, you must have [make](https://www.gnu.org/software/make/) installed and Docker installed and running on your local development machine.
+To use it yourself, you must have [make](https://www.gnu.org/software/make/) installed and Container installed and running on your local development machine.
 
-If you don't have Docker installed, please go to https://www.docker.com/ to install it.
+If you don't have Podman installed, please go to https://podman.io/ to install it.
 
 After you have those dependencies, grab Go dependencies with `make bootstrap`, build your code with `make build` and execute unit tests with `make test`.
 
-## Native Go Development Environment
-
-You can also use the standard `go` toolchain to build and test if you prefer. To do so, you'll need [glide](https://github.com/Masterminds/glide) 0.9 or above and [Go 1.6](http://golang.org) or above installed.
-
-After you have those dependencies, you can build and unit-test your code with `go build` and `go test $(glide nv)`, respectively.
-
-Note that you will not be able to build or push Docker images using this method of development.
-
 # Testing
 
 The Drycc project requires that as much code as possible is unit tested, but the core contributors also recognize that some code must be tested at a higher level (functional or integration tests, for example).
diff --git a/boot.go b/boot.go
index c6000c2..1c4abe8 100644
--- a/boot.go
+++ b/boot.go
@@ -1,6 +1,9 @@
+// Package main provides the entry point for the Drycc builder service.
 package main
 
 import (
+	"context"
+	"fmt"
 	"log"
 	"os"
 	"runtime"
@@ -18,13 +21,13 @@ import (
 	"github.com/drycc/builder/pkg/sys"
 	pkglog "github.com/drycc/pkg/log"
 	"github.com/kelseyhightower/envconfig"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v2"
 )
 
 const (
 	serverConfAppName     = "drycc-builder-server"
 	gitReceiveConfAppName = "drycc-builder-git-receive"
-	gitHomeDir            = "/home/git"
+	gitHomeDir            = "/workspace"
 )
 
 func init() {
@@ -39,16 +42,15 @@ func main() {
 
 	app := cli.NewApp()
 
-	app.Commands = []cli.Command{
+	app.Commands = []*cli.Command{
 		{
 			Name:    "server",
 			Aliases: []string{"srv"},
 			Usage:   "Run the git server",
-			Action: func(c *cli.Context) {
+			Action: func(*cli.Context) error {
 				cnf := new(sshd.Config)
 				if err := envconfig.Process(serverConfAppName, cnf); err != nil {
-					pkglog.Err("getting config for %s [%s]", serverConfAppName, err)
-					os.Exit(1)
+					return fmt.Errorf("getting config for %s [%s]", serverConfAppName, err)
 				}
 				fs := sys.RealFS()
 				env := sys.RealEnv()
@@ -57,21 +59,17 @@ func main() {
 
 				storageParams, err := conf.GetStorageParams(env)
 				if err != nil {
-					log.Printf("Error getting storage parameters (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error getting storage parameters (%s)", err)
 				}
 				var storageDriver storagedriver.StorageDriver
-				storageDriver, err = factory.Create("s3", storageParams)
-
+				storageDriver, err = factory.Create(context.Background(), "s3", storageParams)
 				if err != nil {
-					log.Printf("Error creating storage driver (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error creating storage driver (%s)", err)
 				}
 
 				kubeClient, err := k8s.NewInCluster()
 				if err != nil {
-					log.Printf("Error getting kubernetes client [%s]", err)
-					os.Exit(1)
+					return fmt.Errorf("error getting kubernetes client [%s]", err)
 				}
 				log.Printf("Starting health check server on port %d", cnf.HealthSrvPort)
 				healthSrvCh := make(chan error)
@@ -96,14 +94,11 @@ func main() {
 
 				select {
 				case err := <-healthSrvCh:
-					log.Printf("Error running health server (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error running health server (%s)", err)
 				case i := <-sshCh:
-					log.Printf("Unexpected SSH server stop with code %d", i)
-					os.Exit(i)
+					return fmt.Errorf("unexpected SSH server stop with code %d", i)
 				case err := <-cleanerErrCh:
-					log.Printf("Error running the deleted app cleaner (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error running the deleted app cleaner (%s)", err)
 				}
 			},
 		},
@@ -111,35 +106,32 @@ func main() {
 			Name:    "git-receive",
 			Aliases: []string{"gr"},
 			Usage:   "Run the git-receive hook",
-			Action: func(c *cli.Context) {
+			Action: func(*cli.Context) error {
 				cnf := new(gitreceive.Config)
 				if err := envconfig.Process(gitReceiveConfAppName, cnf); err != nil {
-					log.Printf("Error getting config for %s [%s]", gitReceiveConfAppName, err)
-					os.Exit(1)
+					return fmt.Errorf("error getting config for %s [%s]", gitReceiveConfAppName, err)
 				}
 				cnf.CheckDurations()
-				fs := sys.RealFS()
 				env := sys.RealEnv()
 				storageParams, err := conf.GetStorageParams(env)
 				if err != nil {
-					log.Printf("Error getting storage parameters (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error getting storage parameters (%s)", err)
 				}
 				var storageDriver storagedriver.StorageDriver
-				storageDriver, err = factory.Create("s3", storageParams)
-
+				storageDriver, err = factory.Create(context.Background(), "s3", storageParams)
 				if err != nil {
-					log.Printf("Error creating storage driver (%s)", err)
-					os.Exit(1)
+					return fmt.Errorf("error creating storage driver (%s)", err)
 				}
 
-				if err := gitreceive.Run(cnf, fs, env, storageDriver); err != nil {
-					log.Printf("Error running git receive hook [%s]", err)
-					os.Exit(1)
+				if err := gitreceive.Run(cnf, env, storageDriver); err != nil {
+					return fmt.Errorf("error running git receive hook [%s]", err)
 				}
+				return nil
 			},
 		},
 	}
 
-	app.Run(os.Args)
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
 }
diff --git a/charts/builder/Chart.yaml b/charts/builder/Chart.yaml
index 2e843a7..b68cfcb 100644
--- a/charts/builder/Chart.yaml
+++ b/charts/builder/Chart.yaml
@@ -1,7 +1,13 @@
 name: builder
+apiVersion: v2
+appVersion: 1.0.0
 home: https://github.com/drycc/builder
-version: v1.0.0
+dependencies:
+  - name: common
+    repository: oci://registry.drycc.cc/charts
+    version: ~1.1.2
 description: Git server and application builder for Drycc Workflow.
 maintainers:
   - name: Drycc Team
     email: engineering@drycc.com
+version: v1.1.0
diff --git a/charts/builder/templates/_helpers.tpl b/charts/builder/templates/_helpers.tpl
new file mode 100644
index 0000000..914c773
--- /dev/null
+++ b/charts/builder/templates/_helpers.tpl
@@ -0,0 +1,112 @@
+{{- define "builder.envs" }}
+env:
+- name: "HEALTH_SERVER_PORT"
+  value: "8092"
+- name: "EXTERNAL_PORT"
+  value: "2223"
+- name: "TTL_SECONDS_AFTER_FINISHED"
+  value: "{{ .Values.global.ttlSecondsAfterFinished }}"
+# Set GIT_LOCK_TIMEOUT to number of minutes you want to wait to git push again to the same repository
+- name: "GIT_LOCK_TIMEOUT"
+  value: "30"
+- name: IMAGEBUILDER_IMAGE_PULL_POLICY
+  valueFrom:
+    configMapKeyRef:
+      name: imagebuilder-config
+      key: imagePullPolicy
+- name: "DRYCC_DEBUG"
+  value: "false"
+- name: "POD_NAMESPACE"
+  valueFrom:
+    fieldRef:
+      fieldPath: metadata.namespace
+- name: "DRYCC_CONTROLLER_URL"
+  value: http://drycc-controller-api
+{{- if (.Values.storageEndpoint) }}
+- name: "DRYCC_STORAGE_BUCKET"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: storage-bucket
+- name: "DRYCC_STORAGE_ENDPOINT"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: storage-endpoint
+- name: "DRYCC_STORAGE_ACCESSKEY"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: storage-accesskey
+- name: "DRYCC_STORAGE_SECRETKEY"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: storage-secretkey
+- name: "DRYCC_STORAGE_PATH_STYLE"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: storage-path-style
+{{- else if .Values.storage.enabled  }}
+- name: "DRYCC_STORAGE_BUCKET"
+  value: "builder"
+- name: "DRYCC_STORAGE_ENDPOINT"
+  value: http://drycc-storage:9000
+- name: "DRYCC_STORAGE_ACCESSKEY"
+  valueFrom:
+    secretKeyRef:
+      name: storage-creds
+      key: accesskey
+- name: "DRYCC_STORAGE_SECRETKEY"
+  valueFrom:
+    secretKeyRef:
+      name: storage-creds
+      key: secretkey
+- name: "DRYCC_STORAGE_PATH_STYLE"
+  value: "true"
+{{- end }}
+- name: "DRYCC_REGISTRY_LOCATION"
+  value: {{ ternary "on-cluster" "off-cluster" .Values.registry.enabled }}
+{{- if (.Values.registryHost) }}
+- name: "DRYCC_REGISTRY_HOST"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: registry-host
+- name: "DRYCC_REGISTRY_USERNAME"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: registry-username
+- name: "DRYCC_REGISTRY_PASSWORD"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: registry-password
+- name: "DRYCC_REGISTRY_ORGANIZATION"
+  valueFrom:
+    secretKeyRef:
+      name: builder-secret
+      key: registry-organization
+{{- else if .Values.registry.enabled  }}
+- name: "DRYCC_REGISTRY_HOST"
+  value: drycc-registry:5000
+- name: "DRYCC_REGISTRY_PROXY_HOST"
+  value: {{ print "127.0.0.1"  ":" .Values.registry.proxy.port }}
+- name: "DRYCC_REGISTRY_USERNAME"
+  valueFrom:
+    secretKeyRef:
+      name: registry-secret
+      key: username
+- name: "DRYCC_REGISTRY_PASSWORD"
+  valueFrom:
+    secretKeyRef:
+      name: registry-secret
+      key: password
+{{- end }}
+{{- if (.Values.builderPodNodeSelector) }}
+- name: BUILDER_POD_NODE_SELECTOR
+  value: {{.Values.builderPodNodeSelector}}
+{{- end }}
+{{- end }}
diff --git a/charts/builder/templates/builder-clusterrole.yaml b/charts/builder/templates/builder-clusterrole.yaml
index 97bf437..8c9d9a4 100644
--- a/charts/builder/templates/builder-clusterrole.yaml
+++ b/charts/builder/templates/builder-clusterrole.yaml
@@ -1,8 +1,7 @@
-{{- if (.Values.global.use_rbac) -}}
 kind: ClusterRole
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: drycc:drycc-builder
+  name: {{ printf "%s:drycc-builder" .Release.Namespace | quote }}
   labels:
     app: drycc-builder
     heritage: drycc
@@ -13,4 +12,3 @@ rules:
 - apiGroups: [""]
   resources: ["secrets"]
   verbs: ["list","get"]
-{{- end -}}
diff --git a/charts/builder/templates/builder-clusterrolebinding.yaml b/charts/builder/templates/builder-clusterrolebinding.yaml
index 126df32..fdd34cb 100644
--- a/charts/builder/templates/builder-clusterrolebinding.yaml
+++ b/charts/builder/templates/builder-clusterrolebinding.yaml
@@ -1,17 +1,15 @@
-{{- if (.Values.global.use_rbac) -}}
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: drycc:drycc-builder
+  name: {{ printf "%s:drycc-builder" .Release.Namespace | quote }}
   labels:
     app: drycc-builder
     heritage: drycc
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
-  name: drycc:drycc-builder
+  name: {{ printf "%s:drycc-builder" .Release.Namespace | quote }}
 subjects:
 - kind: ServiceAccount
   name: drycc-builder
   namespace: {{ .Release.Namespace }}
-{{- end -}}
diff --git a/charts/builder/templates/builder-controller-secret-key-auth.yaml b/charts/builder/templates/builder-controller-secret-key-auth.yaml
deleted file mode 100644
index 0147cea..0000000
--- a/charts/builder/templates/builder-controller-secret-key-auth.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: builder-key-auth
-  labels:
-    heritage: drycc
-  annotations:
-    "helm.sh/hook": pre-install
-type: Opaque
-data:
-  builder-key: {{ randAlphaNum 64 | b64enc }}
diff --git a/charts/builder/templates/builder-deployment.yaml b/charts/builder/templates/builder-deployment.yaml
index 1d4ed7f..0d6ed8e 100644
--- a/charts/builder/templates/builder-deployment.yaml
+++ b/charts/builder/templates/builder-deployment.yaml
@@ -5,9 +5,9 @@ metadata:
   labels:
     heritage: drycc
   annotations:
-    component.drycc.cc/version: {{ .Values.image_tag }}
+    component.drycc.cc/version: {{ .Values.imageTag }}
 spec:
-  replicas: 1
+  replicas: {{ .Values.replicas }}
   strategy:
     rollingUpdate:
       maxSurge: 1
@@ -18,80 +18,43 @@ spec:
       app: drycc-builder
   template:
     metadata:
-      labels:
+      labels: {{- include "common.labels.standard" . | nindent 8 }}
         app: drycc-builder
     spec:
+      affinity:
+        podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
+        podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
+        nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values ) | nindent 10 }}
       serviceAccount: drycc-builder
       initContainers:
       - name: drycc-builder-init
-        image: docker.io/drycc/python-dev:latest
-        imagePullPolicy: {{.Values.image_pull_policy}}
-        command:
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest
+        imagePullPolicy: {{.Values.imagePullPolicy}}
+        args:
           - netcat
           - -v
-          - -a
-          - $(DRYCC_MINIO_SERVICE_HOST):$(DRYCC_MINIO_SERVICE_PORT)
+          - -u
+          - $(DRYCC_STORAGE_ENDPOINT),$(DRYCC_CONTROLLER_URL)
+        {{- include "builder.envs" . | indent 8 }}
       containers:
       - name: drycc-builder
-        image: {{.Values.image_registry}}/{{.Values.image_org}}/builder:{{.Values.image_tag}}
-        imagePullPolicy: {{.Values.image_pull_policy}}
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/builder:{{.Values.imageTag}}
+        imagePullPolicy: {{.Values.imagePullPolicy}}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- end }}
         ports:
           - containerPort: 2223
             name: ssh
           - containerPort: 8092
             name: healthsrv
-{{- if or (.Values.limits_cpu) (.Values.limits_memory)}}
+        {{- include "builder.envs" . | indent 8 }}
+        {{- with index .Values "resources" }}
         resources:
-          limits:
-{{- if (.Values.limits_cpu) }}
-            cpu: {{.Values.limits_cpu}}
-{{- end}}
-{{- if (.Values.limits_memory) }}
-            memory: {{.Values.limits_memory}}
-{{- end}}
-{{- end}}
-        env:
-          # NOTE(bacongobbler): use drycc/registry_proxy to work around Docker --insecure-registry requirements
-          - name: "DRYCC_REGISTRY_PROXY_HOST"
-            value: "127.0.0.1"
-          - name: "DRYCC_REGISTRY_PROXY_PORT"
-            value: "{{ .Values.global.registry_proxy_port }}"
-          - name: "HEALTH_SERVER_PORT"
-            value: "8092"
-          - name: "EXTERNAL_PORT"
-            value: "2223"
-          - name: BUILDER_STORAGE
-            value: "{{ .Values.global.storage }}"
-          - name: "DRYCC_REGISTRY_LOCATION"
-            value: "{{ .Values.global.registry_location }}"
-          - name: "TTL_SECONDS_AFTER_FINISHED"
-            value: "{{ .Values.global.ttl_seconds_after_finished }}"
-          # Set GIT_LOCK_TIMEOUT to number of minutes you want to wait to git push again to the same repository
-          - name: "GIT_LOCK_TIMEOUT"
-            value: "30"
-          - name: IMAGEBUILDER_IMAGE_PULL_POLICY
-            valueFrom:
-              configMapKeyRef:
-                name: imagebuilder-config
-                key: image_pull_policy
-          # This var needs to be passed so that the minio client (https://github.com/minio/mc) will work in Alpine linux
-          - name: "DOCKERIMAGE"
-            value: "1"
-          - name: "DRYCC_DEBUG"
-            value: "false"
-          - name: "POD_NAMESPACE"
-            valueFrom:
-              fieldRef:
-                fieldPath: metadata.namespace
-          - name: DRYCC_BUILDER_KEY
-            valueFrom:
-              secretKeyRef:
-                name: builder-key-auth
-                key: builder-key
-{{- if (.Values.builder_pod_node_selector) }}
-          - name: BUILDER_POD_NODE_SELECTOR
-            value: {{.Values.builder_pod_node_selector}}
-{{- end}}
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        {{- if not .Values.diagnosticMode.enabled }}
         livenessProbe:
           httpGet:
             path: /healthz
@@ -104,29 +67,24 @@ spec:
             port: 8092
           initialDelaySeconds: 30
           timeoutSeconds: 1
+        {{- end }}
         volumeMounts:
-            - name: builder-key-auth
-              mountPath: /var/run/secrets/api/auth
-              readOnly: true
-            - name: builder-ssh-private-keys
-              mountPath: /var/run/secrets/drycc/builder/ssh
-              readOnly: true
-            - name: objectstore-creds
-              mountPath: /var/run/secrets/drycc/objectstore/creds
-              readOnly: true
-            - name: imagebuilder-config
-              mountPath: /etc/imagebuilder
-              readOnly: true
+          - name: controller-creds
+            mountPath: /var/run/secrets/drycc/controller
+            readOnly: true
+          - name: imagebuilder-config
+            mountPath: /etc/imagebuilder
+            readOnly: true
+          - name: builder-ssh-private-keys
+            mountPath: /var/run/secrets/drycc/builder/ssh
+            readOnly: true
       volumes:
-        - name: builder-key-auth
+        - name: controller-creds
           secret:
-            secretName: builder-key-auth
-        - name: builder-ssh-private-keys
-          secret:
-            secretName: builder-ssh-private-keys
-        - name: objectstore-creds
-          secret:
-            secretName: objectstorage-keyfile
+            secretName: controller-creds
         - name: imagebuilder-config
           configMap:
             name: imagebuilder-config
+        - name: builder-ssh-private-keys
+          secret:
+            secretName: builder-ssh-private-keys
diff --git a/charts/builder/templates/builder-role.yaml b/charts/builder/templates/builder-role.yaml
index f4ba568..8247089 100644
--- a/charts/builder/templates/builder-role.yaml
+++ b/charts/builder/templates/builder-role.yaml
@@ -1,4 +1,3 @@
-{{- if (.Values.global.use_rbac) -}}
 kind: Role
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
@@ -19,4 +18,3 @@ rules:
 - apiGroups: ["batch"]
   resources: ["jobs"]
   verbs: ["create"]
-{{- end -}}
diff --git a/charts/builder/templates/builder-rolebinding.yaml b/charts/builder/templates/builder-rolebinding.yaml
index bc0b9ad..3af7125 100644
--- a/charts/builder/templates/builder-rolebinding.yaml
+++ b/charts/builder/templates/builder-rolebinding.yaml
@@ -1,4 +1,3 @@
-{{- if (.Values.global.use_rbac) -}}
 kind: RoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
@@ -13,4 +12,3 @@ roleRef:
 subjects:
 - kind: ServiceAccount
   name: drycc-builder
-{{- end -}}
diff --git a/charts/builder/templates/builder-secret-ssh-private-keys.yaml b/charts/builder/templates/builder-secret-ssh-private-keys.yaml
index 7ffa3c6..7045376 100644
--- a/charts/builder/templates/builder-secret-ssh-private-keys.yaml
+++ b/charts/builder/templates/builder-secret-ssh-private-keys.yaml
@@ -4,8 +4,6 @@ metadata:
   name: builder-ssh-private-keys
   labels:
     heritage: drycc
-  annotations:
-    "helm.sh/hook": pre-install
 type: Opaque
 data:
   ssh-host-rsa-key: "{{genPrivateKey "rsa" | b64enc}}"
diff --git a/charts/builder/templates/builder-secret.yaml b/charts/builder/templates/builder-secret.yaml
new file mode 100644
index 0000000..501beb1
--- /dev/null
+++ b/charts/builder/templates/builder-secret.yaml
@@ -0,0 +1,21 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: builder-secret
+  labels:
+    heritage: drycc
+type: Opaque
+data:
+  {{- if (.Values.registryHost) }}
+  registry-host: {{ .Values.registryHost | b64enc }}
+  registry-username: {{ .Values.registryUsername | b64enc }}
+  registry-password: {{ .Values.registryPassword | b64enc }}
+  registry-organization: {{ .Values.registryOrganization | b64enc }}
+  {{- end }}
+  {{- if (.Values.storageEndpoint) }}
+  storage-bucket: {{ .Values.storageBucket | b64enc }}
+  storage-endpoint: {{ .Values.storageEndpoint | b64enc }}
+  storage-accesskey: {{ .Values.storageAccesskey | b64enc }}
+  storage-secretkey: {{ .Values.storageSecretkey | b64enc }}
+  storage-path-style: {{ .Values.storagePathStyle | b64enc }}
+  {{- end }}
diff --git a/charts/builder/templates/builder-service.yaml b/charts/builder/templates/builder-service.yaml
index 6ae46c2..dc7e54b 100644
--- a/charts/builder/templates/builder-service.yaml
+++ b/charts/builder/templates/builder-service.yaml
@@ -2,6 +2,10 @@ apiVersion: v1
 kind: Service
 metadata:
   name: drycc-builder
+  annotations:
+    {{- with .Values.service.annotations }}
+    {{- toYaml . | nindent 4 }}
+    {{- end }}
   labels:
     heritage: drycc
 spec:
@@ -9,9 +13,6 @@ spec:
     - name: ssh
       port: 2222
       targetPort: 2223
-      {{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
-      nodePort: {{ .Values.service.nodePort }}
-      {{- end }}
   selector:
     app: drycc-builder
   type: {{ .Values.service.type }}
diff --git a/charts/builder/values.yaml b/charts/builder/values.yaml
index 73d115e..f62ea33 100644
--- a/charts/builder/values.yaml
+++ b/charts/builder/values.yaml
@@ -1,20 +1,79 @@
-image_org: "drycc"
-image_pull_policy: "Always"
-image_tag: "canary"
-image_registry: "docker.io"
+imageOrg: "drycc"
+imagePullPolicy: "Always"
+imageTag: "canary"
+imageRegistry: "registry.drycc.cc"
+
+replicas: 1
+
+## Enable diagnostic mode
+##
+diagnosticMode:
+  ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden)
+  ##
+  enabled: false
+  ## @param diagnosticMode.command Command to override all containers
+  ##
+  command:
+    - sleep
+  ## @param diagnosticMode.args Args to override all containers
+  ##
+  args:
+    - infinity
+
 service:
+  # Provide any additional service annotations
+  annotations: {}
   # Service type default to LoadBalancer
   type: ClusterIP
-  # If service.type is not set to NodePort, the following statement will be ignored.
-  nodePort: ""
-# limits_cpu: "100m"
-# limits_memory: "50Mi"
-# builder_pod_node_selector: "disk:ssd"
+
+resources: {}
+  # limits:
+  #   cpu: 200m
+  #   memory: 50Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 30Mi
+
+nodeAffinityPreset:
+  key: "drycc.cc/node"
+  type: "soft"
+  values:
+  - "true"
+
+podAffinityPreset:
+  type: ""
+  extraMatchLabels:
+    security: "drycc-security"
+
+podAntiAffinityPreset:
+  type: "soft"
+  extraMatchLabels:
+    app: "drycc-builder"
+
+# builderPodNodeSelector: "drycc.cc/node:true"
 
 # When the TTL controller cleans up the Job. default: 6h
 # see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#ttl-mechanism-for-finished-jobs
-ttl_seconds_after_finished: 21600
+ttlSecondsAfterFinished: 21600
+
+# The following parameters will no longer use the built-in storage component.
+storageBucket: "registry"
+storageEndpoint: ""
+storageAccesskey: ""
+storageSecretkey: ""
+storagePathStyle: "true"
+
+# The following parameters will no longer use the built-in registry component.
+registryHost: ""
+registryUsername: ""
+registryPassword: ""
+registryOrganization: ""
+
+storage:
+  enabled: true
 
-global:
-  # Role-Based Access Control for Kubernetes >= 1.5
-  use_rbac: false
+# host port for the registry proxy in the daemonset
+registry:
+  enabled: true
+  proxy:
+    port: 5555
diff --git a/go.mod b/go.mod
index 425f6f8..fdb9642 100644
--- a/go.mod
+++ b/go.mod
@@ -1,65 +1,101 @@
 module github.com/drycc/builder
 
-go 1.17
+go 1.25
 
 require (
-	github.com/arschles/assert v1.0.1-0.20191213221312-71f210f9375a
-	github.com/aws/aws-sdk-go v1.40.15
-	github.com/distribution/distribution/v3 v3.0.0-20210921140121-a60a3f69eb54
-	github.com/drycc/controller-sdk-go v0.0.0-20210826011521-d57f3c75935a
-	github.com/drycc/pkg v0.0.0-20210826011456-c60b87108840
+	github.com/aws/aws-sdk-go v1.55.6
+	github.com/distribution/distribution/v3 v3.0.0
+	github.com/drycc/controller-sdk-go v0.0.0-20250930075415-578495a55414
+	github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf
+	github.com/google/uuid v1.6.0
 	github.com/kelseyhightower/envconfig v1.4.0
-	github.com/pborman/uuid v1.2.1
-	github.com/urfave/cli v1.22.5
-	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
-	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.22.0
-	k8s.io/apimachinery v0.22.0
-	k8s.io/client-go v0.22.0
+	github.com/stretchr/testify v1.10.0
+	github.com/urfave/cli/v2 v2.27.6
+	golang.org/x/crypto v0.45.0
+	gopkg.in/yaml.v3 v3.0.1
+	k8s.io/api v0.32.3
+	k8s.io/apimachinery v0.32.3
+	k8s.io/client-go v0.32.3
 )
 
 require (
-	github.com/PuerkitoBio/purell v1.1.1 // indirect
-	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
-	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/docker/go-metrics v0.0.1 // indirect
-	github.com/go-logr/logr v0.4.0 // indirect
+	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+	github.com/fxamacker/cbor/v2 v2.8.0 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-openapi/jsonpointer v0.21.1 // indirect
+	github.com/go-openapi/jsonreference v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang/protobuf v1.5.2 // indirect
-	github.com/google/go-cmp v0.5.5 // indirect
-	github.com/google/gofuzz v1.1.0 // indirect
-	github.com/google/uuid v1.1.2 // indirect
-	github.com/googleapis/gnostic v0.5.5 // indirect
-	github.com/gorilla/mux v1.8.0 // indirect
-	github.com/goware/urlx v0.3.1 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/google/gnostic-models v0.6.9 // indirect
+	github.com/google/go-cmp v0.7.0 // indirect
+	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/gorilla/mux v1.8.1 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
-	github.com/json-iterator/go v1.1.11 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.18.0 // indirect
+	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
-	github.com/modern-go/reflect2 v1.0.1 // indirect
-	github.com/prometheus/client_golang v1.1.0 // indirect
-	github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
-	github.com/prometheus/common v0.6.0 // indirect
-	github.com/prometheus/procfs v0.0.3 // indirect
-	github.com/russross/blackfriday/v2 v2.0.1 // indirect
-	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
-	github.com/sirupsen/logrus v1.8.1 // indirect
-	golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
-	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
-	golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
-	golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
-	golang.org/x/text v0.3.6 // indirect
-	golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
-	google.golang.org/appengine v1.6.5 // indirect
-	google.golang.org/protobuf v1.26.0 // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/prometheus/client_golang v1.21.1 // indirect
+	github.com/prometheus/client_model v0.6.1 // indirect
+	github.com/prometheus/common v0.63.0 // indirect
+	github.com/prometheus/procfs v0.16.0 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
+	github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
+	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+	go.opentelemetry.io/contrib/bridges/prometheus v0.60.0 // indirect
+	go.opentelemetry.io/contrib/exporters/autoexport v0.60.0 // indirect
+	go.opentelemetry.io/otel v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect
+	go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0 // indirect
+	go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 // indirect
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 // indirect
+	go.opentelemetry.io/otel/log v0.11.0 // indirect
+	go.opentelemetry.io/otel/metric v1.35.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.35.0 // indirect
+	go.opentelemetry.io/otel/sdk/log v0.11.0 // indirect
+	go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
+	go.opentelemetry.io/otel/trace v1.35.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+	golang.org/x/net v0.47.0 // indirect
+	golang.org/x/oauth2 v0.28.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/term v0.37.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
+	golang.org/x/time v0.11.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
+	google.golang.org/grpc v1.71.1 // indirect
+	google.golang.org/protobuf v1.36.6 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
-	k8s.io/klog/v2 v2.9.0 // indirect
-	k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
-	sigs.k8s.io/yaml v1.2.0 // indirect
+	k8s.io/klog/v2 v2.130.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
+	k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
+	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+	sigs.k8s.io/randfill v1.0.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
+	sigs.k8s.io/yaml v1.4.0 // indirect
 )
-
-replace github.com/docker/distribution v2.7.1+incompatible => github.com/distribution/distribution v2.7.1+incompatible
diff --git a/go.sum b/go.sum
index 82c266e..58c3918 100644
--- a/go.sum
+++ b/go.sum
@@ -1,108 +1,302 @@
-github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/arschles/assert v1.0.1-0.20191213221312-71f210f9375a h1:AUuANVdPi2cfXwkiBWgLb+NWkMTVD5zdAIjvJg9bCBY=
-github.com/arschles/assert v1.0.1-0.20191213221312-71f210f9375a/go.mod h1:DEpb9dlSxnDdJFQLnLwJHnNNpB+Ssa3mrqTSJ/xuv3E=
-github.com/aws/aws-sdk-go v1.40.15 h1:aqQCwW8meVzLCacWX8NEPg8bBkL0ZlcMSbhwrsg6eNE=
-github.com/aws/aws-sdk-go v1.40.15/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
+github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/distribution/distribution/v3 v3.0.0-20210921140121-a60a3f69eb54 h1:gByaNMg05cfuT4yeZBoI3u1InfZDfBnVvpfEkn11mhU=
-github.com/distribution/distribution/v3 v3.0.0-20210921140121-a60a3f69eb54/go.mod h1:ZDZib/BOniVWcXcsy0voU8gR00znhe5VJm47d3H2Y5g=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM=
+github.com/distribution/distribution/v3 v3.0.0/go.mod h1:tRNuFoZsUdyRVegq8xGNeds4KLjwLCRin/tTo6i1DhU=
 github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
 github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
-github.com/drycc/controller-sdk-go v0.0.0-20210826011521-d57f3c75935a h1:TKz7m1cwN3ktK9bGWorLVSSmkLrDO4qALhdGUxTRToo=
-github.com/drycc/controller-sdk-go v0.0.0-20210826011521-d57f3c75935a/go.mod h1:DmVcssrb6yJMWI8sGT+8Xy3caVR5kSlsctD3a7Zn6FA=
-github.com/drycc/pkg v0.0.0-20210826011456-c60b87108840 h1:0OhP9AQ0mh3q0TMxK4PJTPSFwD/wj0xugiaZ3lnLcNA=
-github.com/drycc/pkg v0.0.0-20210826011456-c60b87108840/go.mod h1:KX1FLm7Fq6FLCsjjRsgfI/bMQuHZXqYf1ZXU9fzJhDw=
-github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
-github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/drycc/controller-sdk-go v0.0.0-20250930075415-578495a55414 h1:wiVKgwQsSEBbrQj92Lor6YknaAwPUVp1s/VmAmS3/lI=
+github.com/drycc/controller-sdk-go v0.0.0-20250930075415-578495a55414/go.mod h1:eHcmYwg81ASlP55/U587xnBZnZoeZnPHXGeQ8nYWnsg=
+github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf h1:CYy3NoPhfFhkGAbEppTOQfY/HC2s0FJDcBgbtRKeweg=
+github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf/go.mod h1:BrrNrNskHKm+nJYhXfGuI114w8nupi0AMo8QZHID7CM=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
+github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
+github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
-github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
-github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/goware/urlx v0.3.1 h1:BbvKl8oiXtJAzOzMqAQ0GfIhf96fKeNEZfm9ocNSUBI=
-github.com/goware/urlx v0.3.1/go.mod h1:h8uwbJy68o+tQXCGZNa9D73WN8n0r9OBae5bUnLcgjw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
 github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
-github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
+github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
+github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
-github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
+github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
+github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
-github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
-golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
-golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM=
+github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
+github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/bridges/prometheus v0.60.0 h1:x7sPooQCwSg27SjtQee8GyIIRTQcF4s7eSkac6F2+VA=
+go.opentelemetry.io/contrib/bridges/prometheus v0.60.0/go.mod h1:4K5UXgiHxV484efGs42ejD7E2J/sIlepYgdGoPXe7hE=
+go.opentelemetry.io/contrib/exporters/autoexport v0.60.0 h1:GuQXpvSXNjpswpweIem84U9BNauqHHi2w1GtNAalvpM=
+go.opentelemetry.io/contrib/exporters/autoexport v0.60.0/go.mod h1:CkmxekdHco4d7thFJNPQ7Mby4jMBgZUclnrxT4e+ryk=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 h1:HMUytBT3uGhPKYY/u/G5MR9itrlSO2SMOsSD3Tk3k7A=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0/go.mod h1:hdDXsiNLmdW/9BF2jQpnHHlhFajpWCEYfM6e5m2OAZg=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 h1:C/Wi2F8wEmbxJ9Kuzw/nhP+Z9XaHYMkyDmXy6yR2cjw=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0/go.mod h1:0Lr9vmGKzadCTgsiBydxr6GEZ8SsZ7Ks53LzjWG5Ar4=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
+go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
+go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
+go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0 h1:k6KdfZk72tVW/QVZf60xlDziDvYAePj5QHwoQvrB2m8=
+go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0/go.mod h1:5Y3ZJLqzi/x/kYtrSrPSx7TFI/SGsL7q2kME027tH6I=
+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=
+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg=
+go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y=
+go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
+go.opentelemetry.io/otel/sdk/log v0.11.0 h1:7bAOpjpGglWhdEzP8z0VXc4jObOiDEwr3IYbhBnjk2c=
+go.opentelemetry.io/otel/sdk/log v0.11.0/go.mod h1:dndLTxZbwBstZoqsJB3kGsRPkpAgaJrWfQg3lhlHFFY=
+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
+golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
+golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
+golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
+golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
+google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
+google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.22.0 h1:elCpMZ9UE8dLdYxr55E06TmSeji9I3KH494qH70/y+c=
-k8s.io/api v0.22.0/go.mod h1:0AoXXqst47OI/L0oGKq9DG61dvGRPXs7X4/B7KyjBCU=
-k8s.io/apimachinery v0.22.0 h1:CqH/BdNAzZl+sr3tc0D3VsK3u6ARVSo3GWyLmfIjbP0=
-k8s.io/apimachinery v0.22.0/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
-k8s.io/client-go v0.22.0 h1:sD6o9O6tCwUKCENw8v+HFsuAbq2jCu8cWC61/ydwA50=
-k8s.io/client-go v0.22.0/go.mod h1:GUjIuXR5PiEv/RVK5OODUsm6eZk7wtSWZSaSJbpFdGg=
-k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
-k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
-k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 h1:imL9YgXQ9p7xmPzHFm/vVd/cF78jad+n4wK1ABwYtMM=
-k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
-sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
-sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
+k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
+k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/pkg/builder.go b/pkg/builder.go
index 40701c4..bfddeee 100644
--- a/pkg/builder.go
+++ b/pkg/builder.go
@@ -1,6 +1,6 @@
 // Package pkg provides common libraries for the Drycc builder.
 //
-// The Drycc builder is responsible for building buildpack and docker images for use in the Drycc
+// The Drycc builder is responsible for building buildpack and container images for use in the Drycc
 // on the Drycc PaaS platform.
 package pkg
 
diff --git a/pkg/cleaner/cleaner.go b/pkg/cleaner/cleaner.go
index 87b4ce8..06eba02 100644
--- a/pkg/cleaner/cleaner.go
+++ b/pkg/cleaner/cleaner.go
@@ -5,23 +5,18 @@ package cleaner
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
 	"time"
 
-	//"github.com/distribution/distribution/v3/context"
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
 	"github.com/drycc/builder/pkg/gitreceive"
 	"github.com/drycc/builder/pkg/k8s"
 	"github.com/drycc/builder/pkg/sys"
 	"github.com/drycc/pkg/log"
 	corev1 "k8s.io/api/core/v1"
-
-	//"k8s.io/apimachinery/pkg/labels"
-	//"k8s.io/apimachinery/pkg/fields"
-	//"k8s.io/apimachinery/pkg/apis/meta/internalversion"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -32,7 +27,7 @@ const (
 // localDirs returns all of the local directories immediately under gitHome that filter returns true for.
 // filter will receive only the names of each of the top level directories (not their fully qualified paths), and should return true if it should be included in the output
 func localDirs(gitHome string, filter func(string) bool) ([]string, error) {
-	fileInfos, err := ioutil.ReadDir(gitHome)
+	fileInfos, err := os.ReadDir(gitHome)
 	if err != nil {
 		return nil, err
 	}
@@ -88,8 +83,7 @@ func dirHasGitSuffix(dir string) bool {
 	return strings.HasSuffix(dir, dotGitSuffix)
 }
 
-func deleteFromObjectStore(app string, storageDriver storagedriver.StorageDriver) error {
-
+func deleteFromStorage(app string, storageDriver storagedriver.StorageDriver) error {
 	// delete all files matching app
 	objs, err := storageDriver.List(context.Background(), "home")
 	if err != nil {
@@ -138,7 +132,7 @@ func Run(gitHome string, nsLister k8s.NamespaceLister, fs sys.FS, pollSleepDurat
 			if err := fs.RemoveAll(dirToDelete); err != nil {
 				log.Err("Cleaner error removing local files for deleted app %s (%s)", dirToDelete, err)
 			}
-			if err := deleteFromObjectStore(appToDelete, storageDriver); err != nil {
+			if err := deleteFromStorage(appToDelete, storageDriver); err != nil {
 				log.Err("Cleaner error removing object store files for deleted app %s (%s)", appToDelete, err)
 			}
 		}
diff --git a/pkg/cleaner/cleaner_test.go b/pkg/cleaner/cleaner_test.go
index a00fb1d..8d64553 100644
--- a/pkg/cleaner/cleaner_test.go
+++ b/pkg/cleaner/cleaner_test.go
@@ -6,8 +6,8 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/arschles/assert"
-	"k8s.io/api/core/v1"
+	"github.com/stretchr/testify/assert"
+	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -28,14 +28,14 @@ func TestDirHasGitSuffix(t *testing.T) {
 
 func TestLocalDirs(t *testing.T) {
 	wd, err := os.Getwd()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	pkgDir, err := filepath.Abs(wd + "/..")
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	lDirs, err := localDirs(pkgDir, func(dir string) bool {
 		// no directories with any dots in them
 		return len(strings.Split(dir, ".")) == 1
 	})
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	expectedPackages := map[string]int{
 		"cleaner":    1,
diff --git a/pkg/conf/config.go b/pkg/conf/config.go
index 928f0df..464aee9 100644
--- a/pkg/conf/config.go
+++ b/pkg/conf/config.go
@@ -1,64 +1,59 @@
+// Package conf provides configuration utilities for the builder service.
 package conf
 
 import (
 	"fmt"
-	"io/ioutil"
+	"net"
+	"net/url"
+	"os"
 	"strings"
 
 	"github.com/drycc/builder/pkg/sys"
 )
 
 const (
-	storageCredLocation = "/var/run/secrets/drycc/objectstore/creds/"
-	minioHostEnvVar     = "DRYCC_MINIO_SERVICE_HOST"
-	minioPortEnvVar     = "DRYCC_MINIO_SERVICE_PORT"
-	gcsKey              = "key.json"
+	storageBucketEnvVar    = "DRYCC_STORAGE_BUCKET"
+	storageEndpointEnvVar  = "DRYCC_STORAGE_ENDPOINT"
+	storageAccesskeyEnvVar = "DRYCC_STORAGE_ACCESSKEY"
+	storageSecretkeyEnvVar = "DRYCC_STORAGE_SECRETKEY"
+	storagePathStyleEnvVar = "DRYCC_STORAGE_PATH_STYLE"
 )
 
-// BuilderKeyLocation holds the path of the builder key secret.
-var BuilderKeyLocation = "/var/run/secrets/api/auth/builder-key"
+// ServiceKeyLocation holds the path of the service key secret.
+var ServiceKeyLocation = "/var/run/secrets/drycc/controller/service-key"
 
 // Parameters is map which contains storage params
-type Parameters map[string]interface{}
+type Parameters map[string]any
 
-// GetBuilderKey returns the key to be used as token to interact with drycc-controller
-func GetBuilderKey() (string, error) {
-	builderKeyBytes, err := ioutil.ReadFile(BuilderKeyLocation)
+// GetServiceKey returns the key to be used as token to interact with drycc-controller
+func GetServiceKey() (string, error) {
+	serviceKeyBytes, err := os.ReadFile(ServiceKeyLocation)
 	if err != nil {
-		return "", fmt.Errorf("couldn't get builder key from %s (%s)", BuilderKeyLocation, err)
+		return "", fmt.Errorf("couldn't get builder key from %s (%s)", ServiceKeyLocation, err)
 	}
-	builderKey := strings.Trim(string(builderKeyBytes), "\n")
-	return builderKey, nil
+	serviceKey := strings.TrimSuffix(string(serviceKeyBytes), "\n")
+	return serviceKey, nil
 }
 
 // GetStorageParams returns the credentials required for connecting to object storage
 func GetStorageParams(env sys.Env) (Parameters, error) {
-	params := make(map[string]interface{})
-	files, err := ioutil.ReadDir(storageCredLocation)
-	if err != nil {
-		return nil, err
+	params := make(map[string]any)
+
+	mEndpoint := env.Get(storageEndpointEnvVar)
+	params["regionendpoint"] = mEndpoint
+	region := "us-east-1" // region is required in distribution
+	if endpointURL, err := url.Parse(mEndpoint); err == nil {
+		if endpointURL.Hostname() != "" && net.ParseIP(endpointURL.Hostname()) == nil {
+			region = strings.Split(endpointURL.Hostname(), ".")[0]
+		}
 	}
+	params["region"] = region
 
-	for _, file := range files {
-		if file.IsDir() || file.Name() == "..data" {
-			continue
-		}
-		data, err := ioutil.ReadFile(storageCredLocation + file.Name())
-		if err != nil {
-			return nil, err
-		}
-		//GCS expect the to have the location of the service account credential json file
-		if file.Name() == gcsKey {
-			params["keyfile"] = storageCredLocation + file.Name()
-		} else {
-			params[file.Name()] = string(data)
-		}
+	params["accesskey"] = env.Get(storageAccesskeyEnvVar)
+	params["secretkey"] = env.Get(storageSecretkeyEnvVar)
+	params["bucket"] = env.Get(storageBucketEnvVar)
+	if env.Get(storagePathStyleEnvVar) == "true" {
+		params["forcepathstyle"] = "true"
 	}
-	params["bucket"] = params["builder-bucket"]
-	mHost := env.Get(minioHostEnvVar)
-	mPort := env.Get(minioPortEnvVar)
-	params["region"] = "us-east-1"
-	params["regionendpoint"] = fmt.Sprintf("http://%s:%s", mHost, mPort)
-	params["secure"] = false
 	return params, nil
 }
diff --git a/pkg/conf/config_test.go b/pkg/conf/config_test.go
index a427e2d..42460e7 100644
--- a/pkg/conf/config_test.go
+++ b/pkg/conf/config_test.go
@@ -1,111 +1,59 @@
 package conf
 
 import (
-	"io/ioutil"
 	"os"
-	"os/user"
 	"path/filepath"
 	"testing"
 
-	"github.com/arschles/assert"
 	"github.com/drycc/builder/pkg/sys"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestGetStorageParams(t *testing.T) {
-	usr, err := user.Current()
-	if err != nil {
-		t.Logf("could not retrieve current user: %v", err)
-		t.SkipNow()
-	}
-	if usr.Uid != "0" {
-		t.Logf("current user does not have UID of zero (got %s) "+
-			"so cannot create storage cred location, skipping", usr.Uid)
-		t.SkipNow()
-	}
-
-	if err := os.MkdirAll(storageCredLocation, os.ModeDir); err != nil {
-		t.Fatalf("could not create storage cred location: %v", err)
-	}
-
-	// start by writing out a file to storageCredLocation
-	data := []byte("hello world\n")
-	if err := ioutil.WriteFile(storageCredLocation+"foo", data, 0644); err != nil {
-		t.Fatalf("could not write file to storage cred location: %v", err)
-	}
-
-	params, err := GetStorageParams(sys.NewFakeEnv())
-	if err != nil {
-		t.Errorf("received error while retrieving storage params: %v", err)
-	}
-
-	val, ok := params["foo"]
-	if !ok {
-		t.Error("key foo does not exist in storage params")
-	}
-	if val != string(data) {
-		t.Errorf("expected: %s got: %s", string(data), val)
-	}
-
-	// create a directory inside storage cred location, expecting it to pass
-	if err := os.Mkdir(storageCredLocation+"bar", os.ModeDir); err != nil {
-		t.Fatalf("could not create dir %s: %v", storageCredLocation+"bar", err)
-	}
-
-	_, err = GetStorageParams(sys.NewFakeEnv())
-	if err != nil {
-		t.Errorf("received error while retrieving storage params: %v", err)
-	}
-
-	// create the special "..data" directory symlink, expecting it to pass
-	if err := os.Symlink(storageCredLocation+"bar", storageCredLocation+"..data"); err != nil {
-		t.Fatalf("could not create dir symlink ..data -> %s: %v", storageCredLocation+"bar", err)
-	}
-
-	_, err = GetStorageParams(sys.NewFakeEnv())
-	if err != nil {
-		t.Errorf("received error while retrieving storage params: %v", err)
-	}
-
 	env := sys.NewFakeEnv()
 	env.Envs = map[string]string{
-		"BUILDER_STORAGE":          "minio",
-		"DRYCC_MINIO_SERVICE_HOST": "localhost",
-		"DRYCC_MINIO_SERVICE_PORT": "8088",
+		"DRYCC_STORAGE_BUCKET":     "builder",
+		"DRYCC_STORAGE_ENDPOINT":   "http://localhost:8088",
+		"DRYCC_STORAGE_ACCESSKEY":  "admin",
+		"DRYCC_STORAGE_SECRETKEY":  "adminpass",
+		"DRYCC_STORAGE_PATH_STYLE": "true",
 	}
-	params, err = GetStorageParams(env)
+	params, err := GetStorageParams(env)
 	if err != nil {
 		t.Errorf("received error while retrieving storage params: %v", err)
 	}
+	assert.Equal(t, params["forcepathstyle"], "true", "forcepathstyle")
 	assert.Equal(t, params["regionendpoint"], "http://localhost:8088", "region endpoint")
-	assert.Equal(t, params["secure"], false, "secure")
-	assert.Equal(t, params["region"], "us-east-1", "region")
-	assert.Equal(t, params["bucket"], nil, "bucket")
+	assert.Equal(t, params["region"], "localhost", "region")
+	assert.Equal(t, params["bucket"], "builder", "bucket")
+	assert.Equal(t, params["accesskey"], "admin", "accesskey")
+	assert.Equal(t, params["secretkey"], "adminpass", "secretkey")
 }
 
 func TestGetControllerClient(t *testing.T) {
-	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	tmpDir, err := os.MkdirTemp("", "tmpdir")
 	if err != nil {
 		t.Fatalf("error creating temp directory (%s)", err)
 	}
 
 	defer func() {
 		if err := os.RemoveAll(tmpDir); err != nil {
-			t.Fatalf("failed to remove builder-key from %s (%s)", tmpDir, err)
+			t.Fatalf("failed to remove service-key from %s (%s)", tmpDir, err)
 		}
 	}()
 
-	BuilderKeyLocation = filepath.Join(tmpDir, "builder-key")
+	ServiceKeyLocation = filepath.Join(tmpDir, "service-key")
 	data := []byte("testbuilderkey")
-	if err := ioutil.WriteFile(BuilderKeyLocation, data, 0644); err != nil {
-		t.Fatalf("error creating %s (%s)", BuilderKeyLocation, err)
+	if err := os.WriteFile(ServiceKeyLocation, data, 0o644); err != nil {
+		t.Fatalf("error creating %s (%s)", ServiceKeyLocation, err)
 	}
 
-	key, err := GetBuilderKey()
-	assert.NoErr(t, err)
+	key, err := GetServiceKey()
+	assert.Equal(t, err, nil)
 	assert.Equal(t, key, string(data), "data")
 }
 
-func TestGetBuilderKeyError(t *testing.T) {
-	_, err := GetBuilderKey()
+func TestGetServiceKeyError(t *testing.T) {
+	_, err := GetServiceKey()
 	assert.True(t, err != nil, "no error received when there should have been")
 }
diff --git a/pkg/controller/utils.go b/pkg/controller/utils.go
index 6450195..2edb12b 100644
--- a/pkg/controller/utils.go
+++ b/pkg/controller/utils.go
@@ -1,27 +1,25 @@
+// Package controller provides utilities for interacting with the Drycc controller.
 package controller
 
 import (
-	"fmt"
-
 	"github.com/drycc/builder/pkg/conf"
 	drycc "github.com/drycc/controller-sdk-go"
 	"github.com/drycc/pkg/log"
 )
 
 // New creates a new SDK client configured as the builder.
-func New(host, port string) (*drycc.Client, error) {
-
-	client, err := drycc.New(true, fmt.Sprintf("http://%s:%s/", host, port), "")
+func New(url string) (*drycc.Client, error) {
+	client, err := drycc.New(true, url, "")
 	if err != nil {
 		return client, err
 	}
 	client.UserAgent = "drycc-builder"
 
-	builderKey, err := conf.GetBuilderKey()
+	serviceKey, err := conf.GetServiceKey()
 	if err != nil {
 		return client, err
 	}
-	client.HooksToken = builderKey
+	client.ServiceKey = serviceKey
 
 	return client, nil
 }
diff --git a/pkg/controller/utils_test.go b/pkg/controller/utils_test.go
index c0215c2..63c8494 100644
--- a/pkg/controller/utils_test.go
+++ b/pkg/controller/utils_test.go
@@ -2,54 +2,49 @@ package controller
 
 import (
 	"errors"
-	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"testing"
 
 	builderconf "github.com/drycc/builder/pkg/conf"
-
-	"github.com/arschles/assert"
 	drycc "github.com/drycc/controller-sdk-go"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestNew(t *testing.T) {
-	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	tmpDir, err := os.MkdirTemp("", "tmpdir")
 	if err != nil {
 		t.Fatalf("error creating temp directory (%s)", err)
 	}
 
 	defer func() {
 		if err := os.RemoveAll(tmpDir); err != nil {
-			t.Fatalf("failed to remove builder-key from %s (%s)", tmpDir, err)
+			t.Fatalf("failed to remove service-key from %s (%s)", tmpDir, err)
 		}
 	}()
 
-	builderconf.BuilderKeyLocation = filepath.Join(tmpDir, "builder-key")
+	builderconf.ServiceKeyLocation = filepath.Join(tmpDir, "service-key")
 	data := []byte("testbuilderkey")
-	if err := ioutil.WriteFile(builderconf.BuilderKeyLocation, data, 0644); err != nil {
-		t.Fatalf("error creating %s (%s)", builderconf.BuilderKeyLocation, err)
+	if err := os.WriteFile(builderconf.ServiceKeyLocation, data, 0o644); err != nil {
+		t.Fatalf("error creating %s (%s)", builderconf.ServiceKeyLocation, err)
 	}
 
-	host := "127.0.0.1"
-	port := "80"
-	cli, err := New(host, port)
-	assert.NoErr(t, err)
-	assert.Equal(t, cli.ControllerURL.String(), fmt.Sprintf("http://%s:%s/", host, port), "data")
-	assert.Equal(t, cli.HooksToken, string(data), "data")
+	url := "http://127.0.0.1:80"
+	cli, err := New(url)
+	assert.Equal(t, err, nil)
+	assert.Equal(t, cli.ControllerURL.String(), url, "data")
+	assert.Equal(t, cli.ServiceKey, string(data), "data")
 	assert.Equal(t, cli.UserAgent, "drycc-builder", "user-agent")
 
-	port = "invalid-port-number"
-	if _, err = New(host, port); err == nil {
+	url = "http://127.0.0.1:invalid-port-number"
+	if _, err = New(url); err == nil {
 		t.Errorf("expected error with invalid port number, got nil")
 	}
 }
 
 func TestNewWithInvalidBuilderKeyPath(t *testing.T) {
-	host := "127.0.0.1"
-	port := "80"
-	_, err := New(host, port)
+	url := "http://127.0.0.1:80"
+	_, err := New(url)
 	assert.True(t, err != nil, "no error received when there should have been")
 }
 
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 3182c75..0224457 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -1,3 +1,4 @@
+// Package git provides Git repository handling functionality for the SSH server.
 package git
 
 // This file just contains the Git-specific portions of sshd.
@@ -45,8 +46,8 @@ var preReceiveHookTpl = template.Must(template.New("hooks").Parse(preReceiveHook
 func Receive(
 	repo, operation, gitHome string,
 	channel ssh.Channel,
-	fingerprint, username, conndata, receivetype string) error {
-
+	fingerprint, username, conndata, receivetype string,
+) error {
 	log.Info("receiving git repo name: %s, operation: %s, fingerprint: %s, user: %s", repo, operation, fingerprint, username)
 
 	if receivetype == "mock" {
@@ -54,16 +55,17 @@ func Receive(
 		return nil
 	}
 	repoPath := filepath.Join(gitHome, repo)
+	defer os.RemoveAll(repoPath)
 	log.Info("creating repo directory %s", repoPath)
 	if _, err := createRepo(repoPath); err != nil {
-		err = fmt.Errorf("Did not create new repo (%s)", err)
+		err = fmt.Errorf("did not create new repo (%s)", err)
 
 		return err
 	}
 
 	log.Info("writing pre-receive hook under %s", repoPath)
 	if err := createPreReceiveHook(gitHome, repoPath); err != nil {
-		err = fmt.Errorf("Did not write pre-receive hook (%s)", err)
+		err = fmt.Errorf("did not write pre-receive hook (%s)", err)
 		return err
 	}
 
@@ -93,19 +95,19 @@ func Receive(
 	cmd.Stderr = io.MultiWriter(channel.Stderr(), &errbuff)
 
 	if err := cmd.Start(); err != nil {
-		err = fmt.Errorf("Failed to start git pre-receive hook: %s (%s)", err, errbuff.Bytes())
+		err = fmt.Errorf("failed to start git pre-receive hook: %s (%s)", err, errbuff.Bytes())
 		return err
 	}
 
 	if _, err := io.Copy(inpipe, channel); err != nil {
-		err = fmt.Errorf("Failed to write git objects into the git pre-receive hook (%s)", err)
+		err = fmt.Errorf("failed to write git objects into the git pre-receive hook (%s)", err)
 		return err
 	}
 
 	fmt.Println("Waiting for git-receive to run.")
 	fmt.Println("Waiting for deploy.")
 	if err := cmd.Wait(); err != nil {
-		err = fmt.Errorf("Failed to run git pre-receive hook: %s (%s)", errbuff.Bytes(), err)
+		err = fmt.Errorf("failed to run git pre-receive hook: %s (%s)", errbuff.Bytes(), err)
 		return err
 	}
 	if errbuff.Len() > 0 {
@@ -137,7 +139,7 @@ func createRepo(repoPath string) (bool, error) {
 	} else if os.IsNotExist(err) {
 		log.Debug("Creating new directory at %s", repoPath)
 		// Create directory
-		if err := os.MkdirAll(repoPath, 0755); err != nil {
+		if err := os.MkdirAll(repoPath, 0o755); err != nil {
 			log.Err("Failed to create repository: %s", err)
 			return false, err
 		}
@@ -160,16 +162,16 @@ func createPreReceiveHook(gitHome, repoPath string) error {
 	writePath := filepath.Join(repoPath, "hooks", "pre-receive")
 	fd, err := os.Create(writePath)
 	if err != nil {
-		return fmt.Errorf("Cannot create pre-receive hook file at %s (%s)", writePath, err)
+		return fmt.Errorf("cannot create pre-receive hook file at %s (%s)", writePath, err)
 	}
 	defer fd.Close()
 
 	// parse & generate the template anew each receive for each new git home
 	if err := preReceiveHookTpl.Execute(fd, map[string]string{"GitHome": gitHome}); err != nil {
-		return fmt.Errorf("Cannot write pre-receive hook to %s (%s)", writePath, err)
+		return fmt.Errorf("cannot write pre-receive hook to %s (%s)", writePath, err)
 	}
-	if err := os.Chmod(writePath, 0755); err != nil {
-		return fmt.Errorf("Cannot change pre-receive hook script permissions (%s)", err)
+	if err := os.Chmod(writePath, 0o755); err != nil {
+		return fmt.Errorf("cannot change pre-receive hook script permissions (%s)", err)
 	}
 
 	return nil
diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go
index e20c769..5a87f15 100644
--- a/pkg/git/git_test.go
+++ b/pkg/git/git_test.go
@@ -2,22 +2,21 @@ package git
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
 	"testing"
 
-	"github.com/arschles/assert"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestCreatePreReceiveHook(t *testing.T) {
 	const gitHome = "TestGitHome"
 	gopath := os.Getenv("GOPATH")
 	repoPath := filepath.Join(gopath, "src", "github.com", "drycc", "builder", "testdata")
-	assert.NoErr(t, createPreReceiveHook(gitHome, repoPath))
-	hookBytes, err := ioutil.ReadFile(filepath.Join(repoPath, "hooks", "pre-receive"))
-	assert.NoErr(t, err)
+	assert.Equal(t, createPreReceiveHook(gitHome, repoPath), nil)
+	hookBytes, err := os.ReadFile(filepath.Join(repoPath, "hooks", "pre-receive"))
+	assert.Equal(t, err, nil)
 	hookStr := string(hookBytes)
 	gitHomeIdx := strings.Index(hookStr, fmt.Sprintf("GIT_HOME=%s", gitHome))
 	assert.False(t, gitHomeIdx == -1, "GIT_HOME was not found")
diff --git a/pkg/gitreceive/build.go b/pkg/gitreceive/build.go
index dc4915a..86b9f9a 100644
--- a/pkg/gitreceive/build.go
+++ b/pkg/gitreceive/build.go
@@ -1,28 +1,28 @@
+// Package gitreceive provides functionality for handling git receive operations.
 package gitreceive
 
 import (
 	"bytes"
-	ctx "context"
+	"context"
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
 	"strings"
 
-	"github.com/distribution/distribution/v3/context"
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
 	"github.com/drycc/builder/pkg/controller"
 	"github.com/drycc/builder/pkg/git"
 	"github.com/drycc/builder/pkg/k8s"
 	"github.com/drycc/builder/pkg/sys"
+	drycc "github.com/drycc/controller-sdk-go"
 	dryccAPI "github.com/drycc/controller-sdk-go/api"
 	"github.com/drycc/controller-sdk-go/hooks"
 	"github.com/drycc/pkg/log"
-	"gopkg.in/yaml.v2"
+	"gopkg.in/yaml.v3"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
@@ -58,13 +58,11 @@ func run(cmd *exec.Cmd) error {
 func build(
 	conf *Config,
 	storageDriver storagedriver.StorageDriver,
-	//kubeClient *client.Client,
+	// kubeClient *client.Client,
 	kubeClient *kubernetes.Clientset,
-	fs sys.FS,
 	env sys.Env,
-	builderKey,
-	rawGitSha string) error {
-
+	rawGitSha string,
+) error {
 	// Rewrite regular expression, compatible with slug type
 	storagedriver.PathRegexp = regexp.MustCompile(`^([A-Za-z0-9._:-]*(/[A-Za-z0-9._:-]+)*)+$`)
 
@@ -79,11 +77,11 @@ func build(
 	repoDir := filepath.Join(conf.GitHome, repo)
 	buildDir := filepath.Join(repoDir, "build")
 
-	if err := os.MkdirAll(buildDir, os.ModeDir); err != nil {
+	if err := os.MkdirAll(buildDir, 0o700); err != nil {
 		return fmt.Errorf("making the build directory %s (%s)", buildDir, err)
 	}
 
-	tmpDir, err := ioutil.TempDir(buildDir, "tmp")
+	tmpDir, err := os.MkdirTemp(buildDir, "tmp")
 	if err != nil {
 		return fmt.Errorf("unable to create tmpdir %s (%s)", buildDir, err)
 	}
@@ -93,7 +91,7 @@ func build(
 		}
 	}()
 
-	client, err := controller.New(conf.ControllerHost, conf.ControllerPort)
+	client, err := controller.New(conf.ControllerURL)
 	if err != nil {
 		return err
 	}
@@ -124,7 +122,7 @@ func build(
 
 	stack := getStack(tmpDir, appConf)
 
-	appTgzdata, err := ioutil.ReadFile(absAppTgz)
+	appTgzdata, err := os.ReadFile(absAppTgz)
 	if err != nil {
 		return fmt.Errorf("error while reading file %s: (%s)", appTgz, err)
 	}
@@ -149,16 +147,12 @@ func build(
 
 	imageName := fmt.Sprintf("%s:git-%s", appName, gitSha.Short())
 	buildJobName := imagebuilderJobName(appName, gitSha.Short())
-	registryLocation := conf.RegistryLocation
-	builderImageEnv := make(map[string]string)
-	if registryLocation != "on-cluster" {
-		builderImageEnv, err = getRegistryDetails(kubeClient.CoreV1(), &imageName, registryLocation, conf.PodNamespace)
-		if err != nil {
-			return fmt.Errorf("error getting private registry details %s", err)
-		}
+
+	builderImageEnv, err := getImagebuilderEnv(&imageName, conf, env)
+	if err != nil {
+		return fmt.Errorf("error getting private registry details %s", err)
 	}
 	builderImageEnv["DRYCC_STACK"] = stack["name"]
-	builderImageEnv["DRYCC_REGISTRY_LOCATION"] = registryLocation
 
 	job := createBuilderJob(
 		conf.Debug,
@@ -168,11 +162,8 @@ func build(
 		tarKey,
 		gitSha.Short(),
 		imageName,
-		conf.StorageType,
 		builderName,
 		stack["image"],
-		conf.RegistryHost,
-		conf.RegistryPort,
 		builderImageEnv,
 		imagePullPolicy,
 		securityContext,
@@ -190,7 +181,7 @@ func build(
 	}
 	jobsInterface := kubeClient.BatchV1().Jobs(conf.PodNamespace)
 
-	newJob, err := jobsInterface.Create(ctx.TODO(), job, metav1.CreateOptions{})
+	newJob, err := jobsInterface.Create(context.TODO(), job, metav1.CreateOptions{})
 	if err != nil {
 		return fmt.Errorf("creating builder pod (%s)", err)
 	}
@@ -200,12 +191,12 @@ func build(
 	defer close(stopCh)
 	go pw.Controller.Run(stopCh)
 
-	if err := waitForPod(pw, newJob.Namespace, newJob.Name, conf.SessionIdleInterval(), conf.BuilderPodTickDuration(), conf.BuilderPodWaitDuration()); err != nil {
+	if err := waitForPod(pw, newJob.Name, conf.SessionIdleInterval(), conf.BuilderPodTickDuration(), conf.BuilderPodWaitDuration()); err != nil {
 		return fmt.Errorf("watching events for builder pod startup (%s)", err)
 	}
 
 	options := metav1.ListOptions{
-		LabelSelector: fmt.Sprintf("heritage=%s", newJob.Name),
+		LabelSelector: fmt.Sprintf("job-name=%s", newJob.Name),
 	}
 	podList, err := kubeClient.CoreV1().Pods(newJob.Namespace).List(context.Background(), options)
 	if err != nil {
@@ -217,7 +208,7 @@ func build(
 			Follow: true,
 		}, scheme.ParameterCodec)
 
-	rc, err := req.Stream(ctx.TODO())
+	rc, err := req.Stream(context.TODO())
 	if err != nil {
 		return fmt.Errorf("attempting to stream logs (%s)", err)
 	}
@@ -238,12 +229,12 @@ func build(
 	)
 	// check the state and exit code of the build pod.
 	// if the code is not 0 return error
-	if err := waitForPodEnd(pw, newJob.Namespace, newJob.Name, conf.BuilderPodTickDuration(), conf.BuilderPodWaitDuration()); err != nil {
+	if err := waitForPodEnd(pw, newJob.Name, conf.BuilderPodTickDuration(), conf.BuilderPodWaitDuration()); err != nil {
 		return fmt.Errorf("error getting builder pod status (%s)", err)
 	}
 	log.Debug("Done")
 	log.Debug("Checking for builder pod exit code")
-	buildPod, err := kubeClient.CoreV1().Pods(newJob.Namespace).Get(ctx.TODO(), podList.Items[0].Name, metav1.GetOptions{})
+	buildPod, err := kubeClient.CoreV1().Pods(newJob.Namespace).Get(context.TODO(), podList.Items[0].Name, metav1.GetOptions{})
 	if err != nil {
 		return fmt.Errorf("error getting builder pod status (%s)", err)
 	}
@@ -256,7 +247,7 @@ func build(
 	}
 	log.Debug("Done")
 
-	procfile, err := getProcfile(tmpDir, stack)
+	procfile, err := getProcfile(tmpDir)
 	if err != nil {
 		return err
 	}
@@ -264,22 +255,24 @@ func build(
 	if err != nil {
 		return err
 	}
+	dryccfile, err := drycc.ParseDryccfile(filepath.Join(tmpDir, ".drycc"))
+	if err != nil {
+		return err
+	}
 	log.Info("Build complete.")
 
 	quit := progress("...", conf.SessionIdleInterval())
 	log.Info("Launching App...")
-	release, err := hooks.CreateBuild(client, conf.Username, conf.App(), imageName, stack["name"], gitSha.Short(), procfile, dockerfile)
+	release, err := hooks.CreateBuild(client, conf.Username, conf.App(), imageName, stack["name"], gitSha.Short(), procfile, dryccfile, dockerfile)
 	quit <- true
 	<-quit
 	if controller.CheckAPICompat(client, err) != nil {
-		return fmt.Errorf("The controller returned an error when publishing the release: %s", err)
+		return fmt.Errorf("the controller returned an error when publishing the release: %s", err)
 	}
 
 	log.Info("Done, %s:v%d deployed to Workflow\n", appName, release)
 	log.Info("Use 'drycc open' to view this application in your browser\n")
-	log.Info("To learn more, use 'drycc help' or visit https://drycc.cc/\n")
-
-	run(repoCmd(repoDir, "git", "gc"))
+	log.Info("To learn more, use 'drycc help' or visit https://www.drycc.cc/\n")
 
 	return nil
 }
@@ -290,7 +283,7 @@ func buildBuilderPodNodeSelector(config string) (map[string]string, error) {
 		for _, line := range strings.Split(config, ",") {
 			param := strings.Split(line, ":")
 			if len(param) != 2 {
-				return nil, fmt.Errorf("Invalid BuilderPodNodeSelector value format: %s", config)
+				return nil, fmt.Errorf("invalid BuilderPodNodeSelector value format: %s", config)
 			}
 			selector[strings.TrimSpace(param[0])] = strings.TrimSpace(param[1])
 		}
@@ -298,7 +291,7 @@ func buildBuilderPodNodeSelector(config string) (map[string]string, error) {
 	return selector, nil
 }
 
-func prettyPrintJSON(data interface{}) (string, error) {
+func prettyPrintJSON(data any) (string, error) {
 	output := &bytes.Buffer{}
 	if err := json.NewEncoder(output).Encode(data); err != nil {
 		return "", err
@@ -310,27 +303,28 @@ func prettyPrintJSON(data interface{}) (string, error) {
 	return formatted.String(), nil
 }
 
-func getProcfile(dirName string, stack map[string]string) (dryccAPI.ProcessType, error) {
+func getProcfile(dirName string) (dryccAPI.ProcessType, error) {
 	procfile := dryccAPI.ProcessType{}
-	if _, err := os.Stat(fmt.Sprintf("%s/Procfile", dirName)); err == nil {
-		rawProcFile, err := ioutil.ReadFile(fmt.Sprintf("%s/Procfile", dirName))
+	file := fmt.Sprintf("%s/Procfile", dirName)
+	if _, err := os.Stat(file); err == nil {
+		rawProcFile, err := os.ReadFile(file)
 		if err != nil {
-			return nil, fmt.Errorf("error in reading %s/Procfile (%s)", dirName, err)
+			return nil, fmt.Errorf("error in reading %s (%s)", file, err)
 		}
 		if err := yaml.Unmarshal(rawProcFile, &procfile); err != nil {
-			return nil, fmt.Errorf("procfile %s/ProcFile is malformed (%s)", dirName, err)
+			return nil, fmt.Errorf("procfile %s is malformed (%s)", file, err)
 		}
-		return procfile, nil
 	}
-	return nil, fmt.Errorf("no Procfile can be matched in (%s)", dirName)
+	return procfile, nil
 }
 
 func getDockerfile(dirName string, stack map[string]string) (string, error) {
 	if stack["name"] == "container" {
-		if _, err := os.Stat(fmt.Sprintf("%s/Dockerfile", dirName)); err == nil {
-			rawDockerfile, err := ioutil.ReadFile(fmt.Sprintf("%s/Dockerfile", dirName))
+		file := fmt.Sprintf("%s/Dockerfile", dirName)
+		if _, err := os.Stat(file); err == nil {
+			rawDockerfile, err := os.ReadFile(file)
 			if err != nil {
-				return "", fmt.Errorf("error in reading %s/Dockerfile (%s)", dirName, err)
+				return "", fmt.Errorf("error in reading %s (%s)", file, err)
 			}
 			return string(rawDockerfile), nil
 		}
diff --git a/pkg/gitreceive/build_test.go b/pkg/gitreceive/build_test.go
index ac01f23..450eaa2 100644
--- a/pkg/gitreceive/build_test.go
+++ b/pkg/gitreceive/build_test.go
@@ -2,23 +2,20 @@ package gitreceive
 
 import (
 	"bytes"
-	"fmt"
-	"io/ioutil"
+	"context"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"testing"
 
-	"github.com/arschles/assert"
-
-	//"github.com/distribution/distribution/v3/context"
 	"github.com/distribution/distribution/v3/registry/storage/driver/factory"
 	_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
 	builderconf "github.com/drycc/builder/pkg/conf"
 	"github.com/drycc/builder/pkg/sys"
 	"github.com/drycc/controller-sdk-go/api"
 	"github.com/drycc/pkg/log"
-	"gopkg.in/yaml.v2"
+	"github.com/stretchr/testify/assert"
+	"gopkg.in/yaml.v3"
 )
 
 type testJSONStruct struct {
@@ -33,11 +30,10 @@ type podSelectorBuildCase struct {
 func TestBuild(t *testing.T) {
 	config := &Config{}
 	env := sys.NewFakeEnv()
-	fs := sys.NewFakeFS()
 	// NOTE(bacongobbler): there's a little easter egg here... ;)
 	sha := "0462cef5812ce31fe12f25596ff68dc614c708af"
 
-	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	tmpDir, err := os.MkdirTemp("", "tmpdir")
 	if err != nil {
 		t.Fatalf("error creating temp directory (%s)", err)
 	}
@@ -50,44 +46,43 @@ func TestBuild(t *testing.T) {
 
 	config.GitHome = tmpDir
 
-	storageDriver, err := factory.Create("inmemory", nil)
+	storageDriver, err := factory.Create(context.Background(), "inmemory", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	if err := build(config, storageDriver, nil, fs, env, "foo", sha); err == nil {
+	if err := build(config, storageDriver, nil, env, sha); err == nil {
 		t.Error("expected running build() without setting config.ImagebuilderImagePullPolicy to fail")
 	}
 
 	config.ImagebuilderImagePullPolicy = "Always"
-	if err := build(config, storageDriver, nil, fs, env, "foo", sha); err == nil {
+	if err := build(config, storageDriver, nil, env, sha); err == nil {
 		t.Error("expected running build() without setting config.ImagebuilderImagePullPolicy to fail")
 	}
 
-	err = build(config, storageDriver, nil, fs, env, "foo", "abc123")
+	err = build(config, storageDriver, nil, env, "abc123")
 	expected := "git sha abc123 was invalid"
 	if err.Error() != expected {
 		t.Errorf("expected '%s', got '%v'", expected, err.Error())
 	}
 
-	if err := build(config, storageDriver, nil, fs, env, "foo", sha); err == nil {
+	if err := build(config, storageDriver, nil, env, sha); err == nil {
 		t.Error("expected running build() without valid controller client info to fail")
 	}
 
-	config.ControllerHost = "localhost"
-	config.ControllerPort = "1234"
+	config.ControllerURL = "http://localhost:1234"
 
-	if err := build(config, storageDriver, nil, fs, env, "foo", sha); err == nil {
+	if err := build(config, storageDriver, nil, env, sha); err == nil {
 		t.Error("expected running build() without a valid builder key to fail")
 	}
 
-	builderconf.BuilderKeyLocation = filepath.Join(tmpDir, "builder-key")
+	builderconf.ServiceKeyLocation = filepath.Join(tmpDir, "service-key")
 	data := []byte("testbuilderkey")
-	if err := ioutil.WriteFile(builderconf.BuilderKeyLocation, data, 0644); err != nil {
-		t.Fatalf("error creating %s (%s)", builderconf.BuilderKeyLocation, err)
+	if err := os.WriteFile(builderconf.ServiceKeyLocation, data, 0o644); err != nil {
+		t.Fatalf("error creating %s (%s)", builderconf.ServiceKeyLocation, err)
 	}
 
-	if err := build(config, storageDriver, nil, fs, env, "foo", sha); err == nil {
+	if err := build(config, storageDriver, nil, env, sha); err == nil {
 		t.Error("expected running build() without a valid controller connection to fail")
 	}
 }
@@ -100,12 +95,12 @@ func TestRepoCmd(t *testing.T) {
 }
 
 func TestGetProcfileFromRepoSuccess(t *testing.T) {
-	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	tmpDir, err := os.MkdirTemp("", "tmpdir")
 	if err != nil {
 		t.Fatalf("error creating temp directory (%s)", err)
 	}
 	data := []byte("web: example-go")
-	if err := ioutil.WriteFile(tmpDir+"/Procfile", data, 0644); err != nil {
+	if err := os.WriteFile(tmpDir+"/Procfile", data, 0o644); err != nil {
 		t.Fatalf("error creating %s/Procfile (%s)", tmpDir, err)
 	}
 	defer func() {
@@ -113,24 +108,20 @@ func TestGetProcfileFromRepoSuccess(t *testing.T) {
 			t.Fatalf("failed to remove Procfile from %s (%s)", tmpDir, err)
 		}
 	}()
-	config := api.Config{}
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "buildpack",
-	}
-	procType, err := getProcfile(tmpDir, getStack(tmpDir, config))
+	procType, err := getProcfile(tmpDir)
 	actualData := api.ProcessType{}
 	yaml.Unmarshal(data, &actualData)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	assert.Equal(t, procType, actualData, "data")
 }
 
 func TestGetProcfileFromRepoFailure(t *testing.T) {
-	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	tmpDir, err := os.MkdirTemp("", "tmpdir")
 	if err != nil {
 		t.Fatalf("error creating temp directory (%s)", err)
 	}
 	data := []byte("web= example-go")
-	if err := ioutil.WriteFile(tmpDir+"/Procfile", data, 0644); err != nil {
+	if err := os.WriteFile(tmpDir+"/Procfile", data, 0o644); err != nil {
 		t.Fatalf("error creating %s/Procfile (%s)", tmpDir, err)
 	}
 	defer func() {
@@ -138,38 +129,17 @@ func TestGetProcfileFromRepoFailure(t *testing.T) {
 			t.Fatalf("failed to remove Procfile from %s (%s)", tmpDir, err)
 		}
 	}()
-	config := api.Config{}
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "buildpack",
-	}
-	_, err = getProcfile(tmpDir, getStack(tmpDir, config))
+	_, err = getProcfile(tmpDir)
 
 	assert.True(t, err != nil, "no error received when there should have been")
 }
 
 func TestGetProcfileFromServerSuccess(t *testing.T) {
-	data := []byte("web: example-go")
-	tmpDir := os.TempDir()
-	config := api.Config{}
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "buildpack",
-	}
-
-	_, err := getProcfile("", getStack(tmpDir, config))
+	data := []byte("")
+	expect, _ := getProcfile("")
 	actualData := api.ProcessType{}
 	yaml.Unmarshal(data, &actualData)
-	assert.Err(t, err, fmt.Errorf("no Procfile can be matched in (%s)", ""))
-}
-
-func TestGetProcfileFromServerFailure(t *testing.T) {
-	tmpDir := os.TempDir()
-	config := api.Config{}
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "buildpack",
-	}
-	_, err := getProcfile("", getStack(tmpDir, config))
-	assert.Err(t, err, fmt.Errorf("no Procfile can be matched in (%s)", ""))
-	assert.True(t, err != nil, "no error received when there should have been")
+	assert.Equal(t, expect, actualData)
 }
 
 func TestPrettyPrintJSON(t *testing.T) {
@@ -244,5 +214,5 @@ func TestBuildBuilderPodNodeSelector(t *testing.T) {
 	}
 
 	_, err := buildBuilderPodNodeSelector("invalidformat")
-	assert.ExistsErr(t, err, "invalid format")
+	assert.NotEqual(t, err, nil, "invalid format")
 }
diff --git a/pkg/gitreceive/build_type.go b/pkg/gitreceive/build_type.go
index 7bb31a6..1c19dc3 100644
--- a/pkg/gitreceive/build_type.go
+++ b/pkg/gitreceive/build_type.go
@@ -3,7 +3,6 @@ package gitreceive
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 
 	"github.com/drycc/controller-sdk-go/api"
@@ -14,11 +13,11 @@ import (
 var defaultStacks = `[
 	{
         "name": "buildpack",
-        "image": "drycc/imagebuilder:canary"
+        "image": "registry.drycc.cc/drycc/imagebuilder:canary"
     },
     {
         "name": "container",
-        "image": "drycc/imagebuilder:canary"
+        "image": "registry.drycc.cc/drycc/imagebuilder:canary"
     }
 
 ]`
@@ -28,7 +27,7 @@ var Stacks []map[string]string
 
 // initStack load stack by config
 func initStack() error {
-	data, err := ioutil.ReadFile("/etc/imagebuilder/images.json")
+	data, err := os.ReadFile("/etc/imagebuilder/images.json")
 	if err == nil {
 		return json.Unmarshal(data, &Stacks)
 	}
@@ -42,13 +41,16 @@ func getStack(dirName string, config api.Config) map[string]string {
 	}
 	log.Debug("Stacks: %s", Stacks)
 	log.Debug("Config values %s", config.Values)
-	if stackInterface, ok := config.Values["DRYCC_STACK"]; ok {
-		if strStack, ok := stackInterface.(string); ok {
-			for _, stack := range Stacks {
-				if stack["name"] == strStack {
-					return stack
-				}
-			}
+	strStack := ""
+	for _, v := range config.Values {
+		if v.Group == "global" && v.Name == "DRYCC_STACK" {
+			strStack = v.Value.(string)
+		}
+	}
+
+	for _, stack := range Stacks {
+		if stack["name"] == strStack {
+			return stack
 		}
 	}
 
diff --git a/pkg/gitreceive/build_type_test.go b/pkg/gitreceive/build_type_test.go
index 515bf87..350984c 100644
--- a/pkg/gitreceive/build_type_test.go
+++ b/pkg/gitreceive/build_type_test.go
@@ -35,16 +35,28 @@ func TestGetStack(t *testing.T) {
 			t.Fatalf("failed to remove Procfile from %s (%s)", tmpDir, err)
 		}
 	}()
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "buildpack",
+	config.Values = []api.ConfigValue{
+		{
+			Group: "global",
+			ConfigVar: api.ConfigVar{
+				Name:  "DRYCC_STACK",
+				Value: "buildpack",
+			},
+		},
 	}
 	stack = getStack(tmpDir, config)
 	if stack["name"] != "buildpack" {
 		t.Fatalf("expected procfile build, got %s", stack)
 	}
 
-	config.Values = map[string]interface{}{
-		"DRYCC_STACK": "container",
+	config.Values = []api.ConfigValue{
+		{
+			Group: "global",
+			ConfigVar: api.ConfigVar{
+				Name:  "DRYCC_STACK",
+				Value: "container",
+			},
+		},
 	}
 	stack = getStack(tmpDir, config)
 	if stack["name"] != "container" {
diff --git a/pkg/gitreceive/config.go b/pkg/gitreceive/config.go
index c730833..8d4083d 100644
--- a/pkg/gitreceive/config.go
+++ b/pkg/gitreceive/config.go
@@ -13,13 +13,7 @@ const (
 // Config is the envconfig (http://github.com/kelseyhightower/envconfig) compatible struct for the
 // builder's git-receive hook.
 type Config struct {
-	// k8s service discovery env vars
-	ControllerHost   string `envconfig:"DRYCC_CONTROLLER_SERVICE_HOST" required:"true"`
-	ControllerPort   string `envconfig:"DRYCC_CONTROLLER_SERVICE_PORT" required:"true"`
-	RegistryHost     string `envconfig:"DRYCC_REGISTRY_PROXY_HOST" required:"true"`
-	RegistryPort     string `envconfig:"DRYCC_REGISTRY_PROXY_PORT" required:"true"`
-	RegistryLocation string `envconfig:"DRYCC_REGISTRY_LOCATION" default:"on-cluster"`
-
+	ControllerURL                 string `envconfig:"DRYCC_CONTROLLER_URL" required:"true"`
 	GitHome                       string `envconfig:"GIT_HOME" required:"true"`
 	SSHConnection                 string `envconfig:"SSH_CONNECTION" required:"true"`
 	SSHOriginalCommand            string `envconfig:"SSH_ORIGINAL_COMMAND" required:"true"`
@@ -35,7 +29,6 @@ type Config struct {
 	ObjectStorageWaitDurationMSec int    `envconfig:"OBJECT_STORAGE_WAIT_DURATION" default:"300000"` // 5 minutes
 	SessionIdleIntervalMsec       int    `envconfig:"SESSION_IDLE_INTERVAL" default:"10000"`         // 10 seconds
 	ImagebuilderImagePullPolicy   string `envconfig:"IMAGEBUILDER_IMAGE_PULL_POLICY" default:"Always"`
-	StorageType                   string `envconfig:"BUILDER_STORAGE" default:"minio"`
 	BuilderPodNodeSelector        string `envconfig:"BUILDER_POD_NODE_SELECTOR" default:""`
 }
 
diff --git a/pkg/gitreceive/imagebuilder.go b/pkg/gitreceive/imagebuilder.go
new file mode 100644
index 0000000..46c19be
--- /dev/null
+++ b/pkg/gitreceive/imagebuilder.go
@@ -0,0 +1,59 @@
+package gitreceive
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/drycc/builder/pkg/sys"
+)
+
+var requiredEnvNames = []string{
+	"DRYCC_STORAGE_BUCKET",
+	"DRYCC_STORAGE_ENDPOINT",
+	"DRYCC_STORAGE_PATH_STYLE",
+	"DRYCC_REGISTRY_HOST",
+}
+
+func checkImagebuilderRequiredEnv(imagebuilderEnv map[string]string) error {
+	for index := range requiredEnvNames {
+		envName := requiredEnvNames[index]
+		if _, hasKey := imagebuilderEnv[envName]; !hasKey {
+			msg := fmt.Sprintf("the environment variable %s is required", envName)
+			return errors.New(msg)
+		}
+	}
+	if imagebuilderEnv["DRYCC_REGISTRY_LOCATION"] == "off-cluster" {
+		if imagebuilderEnv["DRYCC_REGISTRY_ORGANIZATION"] == "" {
+			return errors.New("the environment variable DRYCC_REGISTRY_ORGANIZATION is required")
+		}
+	} else {
+		if imagebuilderEnv["DRYCC_REGISTRY_PROXY_HOST"] == "" {
+			return errors.New("the environment variable DRYCC_REGISTRY_PROXY_HOST is required")
+		}
+	}
+	return nil
+}
+
+func getImagebuilderEnv(image *string, config *Config, env sys.Env) (map[string]string, error) {
+	imagebuilderEnv := env.Environ([]string{"DRYCC_REGISTRY_", "DRYCC_STORAGE_"})
+	if err := checkImagebuilderRequiredEnv(imagebuilderEnv); err != nil {
+		return nil, err
+	}
+	if imagebuilderEnv["DRYCC_REGISTRY_LOCATION"] == "off-cluster" {
+		*image = fmt.Sprintf(
+			"%s/%s/%s",
+			imagebuilderEnv["DRYCC_REGISTRY_HOST"],
+			imagebuilderEnv["DRYCC_REGISTRY_ORGANIZATION"],
+			*image,
+		)
+	} else {
+		imagebuilderEnv["DRYCC_REGISTRY_ORGANIZATION"] = config.App()
+		*image = fmt.Sprintf(
+			"%s/%s/%s",
+			imagebuilderEnv["DRYCC_REGISTRY_PROXY_HOST"],
+			imagebuilderEnv["DRYCC_REGISTRY_ORGANIZATION"],
+			*image,
+		)
+	}
+	return imagebuilderEnv, nil
+}
diff --git a/pkg/gitreceive/imagebuilder_test.go b/pkg/gitreceive/imagebuilder_test.go
new file mode 100644
index 0000000..f498002
--- /dev/null
+++ b/pkg/gitreceive/imagebuilder_test.go
@@ -0,0 +1,91 @@
+package gitreceive
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/drycc/builder/pkg/sys"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetImagebuilderEnvOffclusterErr(t *testing.T) {
+	image := "test-image"
+	config := &Config{
+		Repository: "python-getting-started.git",
+	}
+	env := sys.NewFakeEnv()
+	env.Envs = map[string]string{
+		"DRYCC_STORAGE_BUCKET":     "builder",
+		"DRYCC_STORAGE_ENDPOINT":   "drycc-storage",
+		"DRYCC_STORAGE_PATH_STYLE": "true",
+		"DRYCC_REGISTRY_LOCATION":  "off-cluster",
+	}
+	_, err := getImagebuilderEnv(&image, config, env)
+	assert.Error(t, err, errors.New("the environment variable DRYCC_REGISTRY_HOST is required"))
+	env.Envs["DRYCC_REGISTRY_HOST"] = "drycc-registry"
+	_, err = getImagebuilderEnv(&image, config, env)
+	assert.Error(t, err, errors.New("the environment variable DRYCC_REGISTRY_ORGANIZATION is required"))
+}
+
+func TestGetImagebuilderEnvOffclusterSuccess(t *testing.T) {
+	env := sys.NewFakeEnv()
+	env.Envs = map[string]string{
+		"DRYCC_STORAGE_BUCKET":        "builder",
+		"DRYCC_STORAGE_ENDPOINT":      "drycc-storage",
+		"DRYCC_STORAGE_PATH_STYLE":    "true",
+		"DRYCC_REGISTRY_HOST":         "quay.io",
+		"DRYCC_REGISTRY_ORGANIZATION": "kmala",
+		"DRYCC_REGISTRY_LOCATION":     "off-cluster",
+	}
+	expectedData := map[string]string{
+		"DRYCC_STORAGE_BUCKET":        "builder",
+		"DRYCC_STORAGE_ENDPOINT":      "drycc-storage",
+		"DRYCC_STORAGE_PATH_STYLE":    "true",
+		"DRYCC_REGISTRY_LOCATION":     "off-cluster",
+		"DRYCC_REGISTRY_HOST":         "quay.io",
+		"DRYCC_REGISTRY_ORGANIZATION": "kmala",
+	}
+	config := &Config{
+		Repository: "python-getting-started.git",
+	}
+	expectedImage := "quay.io/kmala/test-image"
+
+	image := "test-image"
+	imagebuilderEnv, err := getImagebuilderEnv(&image, config, env)
+	assert.Equal(t, err, nil)
+	assert.Equal(t, expectedData, imagebuilderEnv, "registry details")
+
+	assert.Equal(t, expectedImage, image, "image")
+}
+
+func TestGetImagebuilderEnvOnclusterSuccess(t *testing.T) {
+	env := sys.NewFakeEnv()
+	env.Envs = map[string]string{
+		"DRYCC_STORAGE_BUCKET":      "builder",
+		"DRYCC_STORAGE_ENDPOINT":    "drycc-storage",
+		"DRYCC_STORAGE_PATH_STYLE":  "true",
+		"DRYCC_REGISTRY_HOST":       "drycc-registry",
+		"DRYCC_REGISTRY_PROXY_HOST": "127.0.0.1:8000",
+		"DRYCC_REGISTRY_LOCATION":   "on-cluster",
+	}
+	expectedData := map[string]string{
+		"DRYCC_STORAGE_BUCKET":        "builder",
+		"DRYCC_STORAGE_ENDPOINT":      "drycc-storage",
+		"DRYCC_STORAGE_PATH_STYLE":    "true",
+		"DRYCC_REGISTRY_HOST":         "drycc-registry",
+		"DRYCC_REGISTRY_LOCATION":     "on-cluster",
+		"DRYCC_REGISTRY_PROXY_HOST":   "127.0.0.1:8000",
+		"DRYCC_REGISTRY_ORGANIZATION": "python-getting-started",
+	}
+	config := &Config{
+		Repository: "python-getting-started.git",
+	}
+	expectedImage := "127.0.0.1:8000/python-getting-started/python-getting-started-web:v1.2.1"
+
+	image := "python-getting-started-web:v1.2.1"
+	imagebuilderEnv, err := getImagebuilderEnv(&image, config, env)
+	assert.Equal(t, err, nil)
+	assert.Equal(t, expectedData, imagebuilderEnv, "registry details")
+
+	assert.Equal(t, expectedImage, image, "image")
+}
diff --git a/pkg/gitreceive/k8s_util.go b/pkg/gitreceive/k8s_util.go
index 59cc72c..bcab989 100644
--- a/pkg/gitreceive/k8s_util.go
+++ b/pkg/gitreceive/k8s_util.go
@@ -2,14 +2,14 @@ package gitreceive
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"os"
 	"strconv"
 	"time"
 
 	"github.com/drycc/builder/pkg/k8s"
-	"github.com/pborman/uuid"
+	"github.com/drycc/controller-sdk-go/api"
+	"github.com/google/uuid"
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -23,15 +23,12 @@ const (
 	tarPath                = "TAR_PATH"
 	debugKey               = "DRYCC_DEBUG"
 	sourceVersion          = "SOURCE_VERSION"
-	objectStore            = "objectstorage-keyfile"
-	builderStorage         = "BUILDER_STORAGE"
-	objectStorePath        = "/var/run/secrets/drycc/objectstore/creds"
 	imagebuilderConfig     = "imagebuilder-config"
 	imagebuilderConfigPath = "/etc/imagebuilder"
 )
 
 func imagebuilderJobName(appName, shortSha string) string {
-	uid := uuid.New()[:8]
+	uid := uuid.New().String()[:8]
 	// NOTE(bacongobbler): pod names cannot exceed 63 characters in length, so we truncate
 	// the application name to stay under that limit when adding all the extra metadata to the name
 	if len(appName) > 33 {
@@ -44,45 +41,24 @@ func createBuilderJob(
 	debug bool,
 	name,
 	namespace string,
-	env map[string]interface{},
+	config []api.ConfigValue,
 	tarKey,
 	gitShortHash string,
 	imageName,
-	storageType,
 	builderName,
-	builderImage,
-	registryHost,
-	registryPort string,
+	builderImage string,
 	builderImageEnv map[string]string,
 	pullPolicy corev1.PullPolicy,
 	securityContext corev1.SecurityContext,
 	nodeSelector map[string]string,
 ) *batchv1.Job {
-
-	job := buildJob(debug, name, namespace, pullPolicy, securityContext, nodeSelector, env)
-
-	// inject application envvars as a special envvar which will be handled by imagebuilder to
-	// inject them as build-time variables.
-	// NOTE(bacongobbler): image-py takes buildargs as a json string in the form of
-	//
-	// {"KEY": "value"}
-	//
-	// So we need to translate the map into json.
-	if _, ok := env["DRYCC_DOCKER_BUILD_ARGS_ENABLED"]; ok {
-		imageBuildArgs, _ := json.Marshal(env)
-		addEnvToJob(job, "DOCKER_BUILD_ARGS", string(imageBuildArgs))
-	}
+	job := buildJob(debug, name, namespace, builderName, pullPolicy, securityContext, nodeSelector, config)
 	job.Spec.Template.Spec.Containers[0].Name = builderName
 	job.Spec.Template.Spec.Containers[0].Image = builderImage
 
 	addEnvToJob(job, tarPath, tarKey)
 	addEnvToJob(job, sourceVersion, gitShortHash)
-	addEnvToJob(job, "IMG_NAME", imageName)
-	addEnvToJob(job, builderStorage, storageType)
-	// inject existing DRYCC_REGISTRY_PROXY_HOST and PORT info to imagebuilder
-	// see https://github.com/drycc/imagebuilder/issues/83
-	addEnvToJob(job, "DRYCC_REGISTRY_PROXY_HOST", registryHost)
-	addEnvToJob(job, "DRYCC_REGISTRY_PROXY_PORT", registryPort)
+	addEnvToJob(job, "IMAGE_NAME", imageName)
 
 	for key, value := range builderImageEnv {
 		addEnvToJob(job, key, value)
@@ -98,12 +74,13 @@ func newInt32(i int32) *int32 {
 func buildJob(
 	debug bool,
 	name,
-	namespace string,
-	//pullPolicy api.PullPolicy,
+	namespace,
+	builderName string,
 	pullPolicy corev1.PullPolicy,
 	securityContext corev1.SecurityContext,
 	nodeSelector map[string]string,
-	env map[string]interface{}) batchv1.Job {
+	values []api.ConfigValue,
+) batchv1.Job {
 	TTLSecondsAfterFinished := newInt32(21600)
 	if os.Getenv("TTL_SECONDS_AFTER_FINISHED") != "" {
 		ttl, err := strconv.ParseInt(os.Getenv("TTL_SECONDS_AFTER_FINISHED"), 10, 32)
@@ -119,7 +96,9 @@ func buildJob(
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Labels: map[string]string{
-						"heritage": name,
+						"app":      builderName,
+						"job-name": name,
+						"heritage": "drycc",
 					},
 				},
 				Spec: corev1.PodSpec{
@@ -136,29 +115,16 @@ func buildJob(
 			Name:      name,
 			Namespace: namespace,
 			Labels: map[string]string{
-				"heritage": name,
+				"app":      builderName,
+				"job-name": name,
+				"heritage": "drycc",
 			},
 		},
 	}
 	job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever
 	job.Spec.Template.Spec.Containers[0].ImagePullPolicy = pullPolicy
 	job.Spec.Template.Spec.Containers[0].SecurityContext = &securityContext
-	job.Spec.Template.Spec.Volumes = append(job.Spec.Template.Spec.Volumes, corev1.Volume{
-		Name: objectStore,
-		VolumeSource: corev1.VolumeSource{
-			Secret: &corev1.SecretVolumeSource{
-				SecretName: objectStore,
-			},
-		},
-	})
 
-	job.Spec.Template.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
-		{
-			Name:      objectStore,
-			MountPath: objectStorePath,
-			ReadOnly:  true,
-		},
-	}
 	job.Spec.Template.Spec.Volumes = append(job.Spec.Template.Spec.Volumes, corev1.Volume{
 		Name: imagebuilderConfig,
 		VolumeSource: corev1.VolumeSource{
@@ -177,10 +143,17 @@ func buildJob(
 	})
 
 	if len(job.Spec.Template.Spec.Containers) > 0 {
-		for k, v := range env {
+		cvs := []api.ConfigVar{}
+		// only take the value of the global group
+		for _, v := range values {
+			if v.Group == "global" {
+				cvs = append(cvs, v.ConfigVar)
+			}
+		}
+		for _, v := range cvs {
 			job.Spec.Template.Spec.Containers[0].Env = append(job.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
-				Name:  k,
-				Value: fmt.Sprintf("%v", v),
+				Name:  v.Name,
+				Value: fmt.Sprintf("%v", v.Value),
 			})
 		}
 	}
@@ -206,7 +179,7 @@ func addEnvToJob(job batchv1.Job, key, value string) {
 }
 
 // waitForPod waits for a pod in state running, succeeded or failed
-func waitForPod(pw *k8s.PodWatcher, ns, podName string, ticker, interval, timeout time.Duration) error {
+func waitForPod(pw *k8s.PodWatcher, jobName string, ticker, interval, timeout time.Duration) error {
 	condition := func(pod *corev1.Pod) (bool, error) {
 		if pod.Status.Phase == corev1.PodRunning {
 			return true, nil
@@ -215,20 +188,20 @@ func waitForPod(pw *k8s.PodWatcher, ns, podName string, ticker, interval, timeou
 			return true, nil
 		}
 		if pod.Status.Phase == corev1.PodFailed {
-			return true, fmt.Errorf("Giving up; pod went into failed status: \n[%s]:%s", pod.Status.Reason, pod.Status.Message)
+			return true, fmt.Errorf("giving up; pod went into failed status: \n[%s]:%s", pod.Status.Reason, pod.Status.Message)
 		}
 		return false, nil
 	}
 
 	quit := progress("...", ticker)
-	err := waitForPodCondition(pw, ns, podName, condition, interval, timeout)
+	err := waitForPodCondition(pw, jobName, condition, interval, timeout)
 	quit <- true
 	<-quit
 	return err
 }
 
 // waitForPodEnd waits for a pod in state succeeded or failed
-func waitForPodEnd(pw *k8s.PodWatcher, ns, podName string, interval, timeout time.Duration) error {
+func waitForPodEnd(pw *k8s.PodWatcher, jobName string, interval, timeout time.Duration) error {
 	condition := func(pod *corev1.Pod) (bool, error) {
 		if pod.Status.Phase == corev1.PodSucceeded {
 			return true, nil
@@ -239,32 +212,35 @@ func waitForPodEnd(pw *k8s.PodWatcher, ns, podName string, interval, timeout tim
 		return false, nil
 	}
 
-	return waitForPodCondition(pw, ns, podName, condition, interval, timeout)
+	return waitForPodCondition(pw, jobName, condition, interval, timeout)
 }
 
 // waitForPodCondition waits for a pod in state defined by a condition (func)
-func waitForPodCondition(pw *k8s.PodWatcher, ns, podName string, condition func(pod *corev1.Pod) (bool, error),
-	interval, timeout time.Duration) error {
-	return wait.PollImmediate(interval, timeout, func() (bool, error) {
-		pods, err := pw.Store.List(labels.Set{"heritage": podName}.AsSelector())
+func waitForPodCondition(pw *k8s.PodWatcher, jobName string, condition func(pod *corev1.Pod) (bool, error),
+	interval, timeout time.Duration,
+) error {
+	return wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(context.Context) (done bool, err error) {
+		selector := labels.Set{
+			"job-name": jobName,
+			"heritage": "drycc",
+		}.AsSelector()
+		pods, err := pw.Store.List(selector)
 		if err != nil || len(pods) == 0 {
 			return false, nil
 		}
-
-		done, err := condition(pods[0])
+		done, err = condition(pods[0])
 		if err != nil {
 			return false, err
 		}
 		if done {
 			return true, nil
 		}
-
 		return false, nil
 	})
 }
 
 func progress(msg string, interval time.Duration) chan bool {
-	tick := time.Tick(interval)
+	tick := time.NewTicker(interval)
 	quit := make(chan bool)
 	go func() {
 		for {
@@ -272,7 +248,7 @@ func progress(msg string, interval time.Duration) chan bool {
 			case <-quit:
 				close(quit)
 				return
-			case <-tick:
+			case <-tick.C:
 				fmt.Println(msg)
 			}
 		}
@@ -280,7 +256,7 @@ func progress(msg string, interval time.Duration) chan bool {
 	return quit
 }
 
-func createAppEnvConfigSecret(secretsClient typedcorev1.SecretInterface, secretName string, env map[string]interface{}) error {
+func createAppEnvConfigSecret(secretsClient typedcorev1.SecretInterface, secretName string, env map[string]any) error {
 	newSecret := new(corev1.Secret)
 	newSecret.Name = secretName
 	newSecret.Type = corev1.SecretTypeOpaque
diff --git a/pkg/gitreceive/k8s_util_test.go b/pkg/gitreceive/k8s_util_test.go
index b2b27de..b4aa536 100644
--- a/pkg/gitreceive/k8s_util_test.go
+++ b/pkg/gitreceive/k8s_util_test.go
@@ -6,8 +6,9 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/arschles/assert"
 	"github.com/drycc/builder/pkg/k8s"
+	"github.com/drycc/controller-sdk-go/api"
+	"github.com/stretchr/testify/assert"
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -32,25 +33,28 @@ type imageBuildCase struct {
 	debug                       bool
 	name                        string
 	namespace                   string
-	env                         map[string]interface{}
+	env                         []api.ConfigValue
 	tarKey                      string
 	gitShortHash                string
 	imgName                     string
 	imagebuilderName            string
 	imagebuilderImage           string
 	imagebuilderImagePullPolicy corev1.PullPolicy
-	storageType                 string
 	builderPodNodeSelector      map[string]string
 }
 
 func TestBuildJob(t *testing.T) {
-	emptyEnv := make(map[string]interface{})
-
-	env := make(map[string]interface{})
-	env["KEY"] = "VALUE"
-	buildArgsEnv := make(map[string]interface{})
-	buildArgsEnv["DRYCC_DOCKER_BUILD_ARGS_ENABLED"] = "1"
-	buildArgsEnv["KEY"] = "VALUE"
+	emptyValues := []api.ConfigValue{}
+
+	values := []api.ConfigValue{
+		{
+			Group: "global",
+			ConfigVar: api.ConfigVar{
+				Name:  "KEY",
+				Value: "VALUE",
+			},
+		},
+	}
 	var job *batchv1.Job
 
 	emptyNodeSelector := make(map[string]string)
@@ -63,16 +67,15 @@ func TestBuildJob(t *testing.T) {
 	nodeSelector2["network"] = "fast"
 
 	imageBuilds := []imageBuildCase{
-		{true, "test", "default", emptyEnv, "tar", "deadbeef", "imagebuilder", "", "", corev1.PullAlways, "", nodeSelector1},
-		{true, "test", "default", env, "tar", "deadbeef", "", "imagebuilder", "", corev1.PullAlways, "", nodeSelector2},
-		{true, "test", "default", emptyEnv, "tar", "deadbeef", "img", "imagebuilder", "", corev1.PullAlways, "", emptyNodeSelector},
-		{true, "test", "default", env, "tar", "deadbeef", "img", "imagebuilder", "", corev1.PullAlways, "", emptyNodeSelector},
-		{true, "test", "default", env, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullAlways, "", emptyNodeSelector},
-		{true, "test", "default", env, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullIfNotPresent, "", emptyNodeSelector},
-		{true, "test", "default", env, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullNever, "", nil},
-		{true, "test", "default", buildArgsEnv, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullIfNotPresent, "", emptyNodeSelector},
+		{true, "test", "default", emptyValues, "tar", "deadbeef", "imagebuilder", "", "", corev1.PullAlways, nodeSelector1},
+		{true, "test", "default", values, "tar", "deadbeef", "", "imagebuilder", "", corev1.PullAlways, nodeSelector2},
+		{true, "test", "default", emptyValues, "tar", "deadbeef", "img", "imagebuilder", "", corev1.PullAlways, emptyNodeSelector},
+		{true, "test", "default", values, "tar", "deadbeef", "img", "imagebuilder", "", corev1.PullAlways, emptyNodeSelector},
+		{true, "test", "default", values, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullAlways, emptyNodeSelector},
+		{true, "test", "default", values, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullIfNotPresent, emptyNodeSelector},
+		{true, "test", "default", values, "tar", "deadbeef", "img", "imagebuilder", "customimage", corev1.PullNever, nil},
 	}
-	buildImageEnv := map[string]string{"REG_LOC": "on-cluster"}
+	buildImageEnv := map[string]string{"DRYCC_REGISTRY_LOCATION": "on-cluster"}
 	for _, build := range imageBuilds {
 		job = createBuilderJob(
 			build.debug,
@@ -82,31 +85,25 @@ func TestBuildJob(t *testing.T) {
 			build.tarKey,
 			build.gitShortHash,
 			build.imgName,
-			build.storageType,
 			build.imagebuilderName,
 			build.imagebuilderImage,
-			"localhost",
-			"5555",
 			buildImageEnv,
 			build.imagebuilderImagePullPolicy,
 			k8s.SecurityContextFromPrivileged(false),
 			build.builderPodNodeSelector,
 		)
 
-		if job.ObjectMeta.Name != build.name {
-			t.Errorf("expected %v but returned %v ", build.name, job.ObjectMeta.Name)
+		if job.Name != build.name {
+			t.Errorf("expected %v but returned %v ", build.name, job.Name)
 		}
-		if job.ObjectMeta.Namespace != build.namespace {
-			t.Errorf("expected %v but returned %v ", build.namespace, job.ObjectMeta.Namespace)
+		if job.Namespace != build.namespace {
+			t.Errorf("expected %v but returned %v ", build.namespace, job.Namespace)
 		}
 
 		checkForEnv(t, job, "SOURCE_VERSION", build.gitShortHash)
 		checkForEnv(t, job, "TAR_PATH", build.tarKey)
-		checkForEnv(t, job, "IMG_NAME", build.imgName)
-		checkForEnv(t, job, "REG_LOC", "on-cluster")
-		if _, ok := build.env["DRYCC_DOCKER_BUILD_ARGS_ENABLED"]; ok {
-			checkForEnv(t, job, "DOCKER_BUILD_ARGS", `{"DRYCC_DOCKER_BUILD_ARGS_ENABLED":"1","KEY":"VALUE"}`)
-		}
+		checkForEnv(t, job, "IMAGE_NAME", build.imgName)
+		checkForEnv(t, job, "DRYCC_REGISTRY_LOCATION", "on-cluster")
 		if build.imagebuilderImage != "" {
 			if job.Spec.Template.Spec.Containers[0].Image != build.imagebuilderImage {
 				t.Errorf("expected %v but returned %v", build.imagebuilderImage, job.Spec.Template.Spec.Containers[0].Image)
@@ -154,7 +151,7 @@ func TestCreateAppEnvConfigSecretErr(t *testing.T) {
 		},
 	}
 	err := createAppEnvConfigSecret(secretsClient, "test", nil)
-	assert.Err(t, err, expectedErr)
+	assert.Error(t, err, expectedErr)
 }
 
 func TestCreateAppEnvConfigSecretSuccess(t *testing.T) {
@@ -164,7 +161,7 @@ func TestCreateAppEnvConfigSecretSuccess(t *testing.T) {
 		},
 	}
 	err := createAppEnvConfigSecret(secretsClient, "test", nil)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 }
 
 func TestCreateAppEnvConfigSecretAlreadyExists(t *testing.T) {
@@ -178,5 +175,5 @@ func TestCreateAppEnvConfigSecretAlreadyExists(t *testing.T) {
 		},
 	}
 	err := createAppEnvConfigSecret(secretsClient, "test", nil)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 }
diff --git a/pkg/gitreceive/registry.go b/pkg/gitreceive/registry.go
deleted file mode 100644
index c6becbf..0000000
--- a/pkg/gitreceive/registry.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package gitreceive
-
-import (
-	"context"
-	"strings"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
-)
-
-const (
-	registrySecret = "registry-secret"
-)
-
-func getDetailsFromRegistrySecret(secretGetter typedcorev1.SecretInterface, secret string) (map[string]string, error) {
-	regSecret, err := secretGetter.Get(context.TODO(), secret, metav1.GetOptions{})
-	if err != nil {
-		return nil, err
-	}
-	regDetails := make(map[string]string)
-	for key, value := range regSecret.Data {
-		regDetails[key] = string(value)
-	}
-	return regDetails, nil
-}
-
-func getRegistryDetails(kubeClient typedcorev1.SecretsGetter, image *string, registryLocation, namespace string) (map[string]string, error) {
-	privateRegistrySecretInterface := kubeClient.Secrets(namespace)
-	registryEnv := make(map[string]string)
-	var regSecretData map[string]string
-	var err error
-	if registryLocation == "off-cluster" {
-		regSecretData, err = getDetailsFromRegistrySecret(privateRegistrySecretInterface, registrySecret)
-		if err != nil {
-			return nil, err
-		}
-		for key, value := range regSecretData {
-			registryEnv["DRYCC_REGISTRY_"+strings.ToUpper(key)] = value
-		}
-		if registryEnv["DRYCC_REGISTRY_ORGANIZATION"] != "" {
-			*image = registryEnv["DRYCC_REGISTRY_ORGANIZATION"] + "/" + *image
-		}
-		if registryEnv["DRYCC_REGISTRY_HOSTNAME"] != "" {
-			*image = registryEnv["DRYCC_REGISTRY_HOSTNAME"] + "/" + *image
-		}
-	}
-	return registryEnv, nil
-}
diff --git a/pkg/gitreceive/registry_test.go b/pkg/gitreceive/registry_test.go
deleted file mode 100644
index cb0b7be..0000000
--- a/pkg/gitreceive/registry_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package gitreceive
-
-import (
-	"errors"
-	"testing"
-
-	"github.com/arschles/assert"
-	"github.com/drycc/builder/pkg/k8s"
-	corev1 "k8s.io/api/core/v1"
-	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
-)
-
-const (
-	testSecret     = "test-secret"
-	dryccNamespace = "drycc"
-)
-
-func TestGetDetailsFromRegistrySecretErr(t *testing.T) {
-	expectedErr := errors.New("get secret error")
-	getter := &k8s.FakeSecret{
-		FnGet: func(string) (*corev1.Secret, error) {
-			return &corev1.Secret{}, expectedErr
-		},
-	}
-	_, err := getDetailsFromRegistrySecret(getter, testSecret)
-	assert.Err(t, err, expectedErr)
-}
-
-func TestGetDetailsFromRegistrySecretSuccess(t *testing.T) {
-	data := map[string][]byte{"test": []byte("test")}
-	expectedData := map[string]string{"test": "test"}
-	secret := corev1.Secret{Data: data}
-	getter := &k8s.FakeSecret{
-		FnGet: func(string) (*corev1.Secret, error) {
-			return &secret, nil
-		},
-	}
-	secretData, err := getDetailsFromRegistrySecret(getter, testSecret)
-	assert.NoErr(t, err)
-	assert.Equal(t, secretData, expectedData, "secret data")
-}
-
-func TestGetRegistryDetailsOffclusterErr(t *testing.T) {
-	expectedErr := errors.New("get secret error")
-	getter := &k8s.FakeSecret{
-		FnGet: func(string) (*corev1.Secret, error) {
-			return &corev1.Secret{}, expectedErr
-			//return &kubernetes.Clientset.CoreV1(), expectedErr
-		},
-	}
-
-	kubeClient := &k8s.FakeSecretsGetter{
-		Fn: func(string) typedcorev1.SecretInterface {
-			return getter
-		},
-	}
-	image := "test-image"
-	_, err := getRegistryDetails(kubeClient, &image, "off-cluster", dryccNamespace)
-	assert.Err(t, err, expectedErr)
-}
-
-func TestGetRegistryDetailsOffclusterSuccess(t *testing.T) {
-	data := map[string][]byte{"organization": []byte("kmala"), "hostname": []byte("quay.io")}
-	expectedData := map[string]string{"DRYCC_REGISTRY_HOSTNAME": "quay.io", "DRYCC_REGISTRY_ORGANIZATION": "kmala"}
-	expectedImage := "quay.io/kmala/test-image"
-	secret := corev1.Secret{Data: data}
-	getter := &k8s.FakeSecret{
-		FnGet: func(string) (*corev1.Secret, error) {
-			return &secret, nil
-		},
-	}
-
-	kubeClient := &k8s.FakeSecretsGetter{
-		Fn: func(string) typedcorev1.SecretInterface {
-			return getter
-		},
-	}
-	image := "test-image"
-	regDetails, err := getRegistryDetails(kubeClient, &image, "off-cluster", dryccNamespace)
-	assert.NoErr(t, err)
-	assert.Equal(t, expectedData, regDetails, "registry details")
-	assert.Equal(t, expectedImage, image, "image")
-}
diff --git a/pkg/gitreceive/run.go b/pkg/gitreceive/run.go
index 50d71e6..7468006 100644
--- a/pkg/gitreceive/run.go
+++ b/pkg/gitreceive/run.go
@@ -7,11 +7,9 @@ import (
 	"strings"
 
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
-	builderconf "github.com/drycc/builder/pkg/conf"
+	"github.com/drycc/builder/pkg/k8s"
 	"github.com/drycc/builder/pkg/sys"
 	"github.com/drycc/pkg/log"
-
-	"github.com/drycc/builder/pkg/k8s"
 )
 
 func readLine(line string) (string, string, string, error) {
@@ -24,15 +22,9 @@ func readLine(line string) (string, string, string, error) {
 
 // Run runs the git-receive hook. This func is effectively the main for the git-receive hook,
 // although it is called from the main in boot.go.
-func Run(conf *Config, fs sys.FS, env sys.Env, storageDriver storagedriver.StorageDriver) error {
+func Run(conf *Config, env sys.Env, storageDriver storagedriver.StorageDriver) error {
 	log.Debug("Running git hook")
-
-	builderKey, err := builderconf.GetBuilderKey()
-	if err != nil {
-		return err
-	}
-
-	//kubeClient, err := client.NewInCluster()
+	// kubeClient, err := client.NewInCluster()
 	kubeClient, err := k8s.NewInCluster()
 	if err != nil {
 		return fmt.Errorf("couldn't reach the api server (%s)", err)
@@ -42,7 +34,6 @@ func Run(conf *Config, fs sys.FS, env sys.Env, storageDriver storagedriver.Stora
 	for scanner.Scan() {
 		line := scanner.Text()
 		oldRev, newRev, refName, err := readLine(line)
-
 		if err != nil {
 			return fmt.Errorf("reading STDIN (%s)", err)
 		}
@@ -51,11 +42,10 @@ func Run(conf *Config, fs sys.FS, env sys.Env, storageDriver storagedriver.Stora
 
 		// if we're processing a receive-pack on an existing repo, run a build
 		if strings.HasPrefix(conf.SSHOriginalCommand, "git-receive-pack") {
-			if err := build(conf, storageDriver, kubeClient, fs, env, builderKey, newRev); err != nil {
+			if err := build(conf, storageDriver, kubeClient, env, newRev); err != nil {
 				return err
 			}
 		}
 	}
-
 	return scanner.Err()
 }
diff --git a/pkg/gitreceive/run_test.go b/pkg/gitreceive/run_test.go
index 251169d..16af974 100644
--- a/pkg/gitreceive/run_test.go
+++ b/pkg/gitreceive/run_test.go
@@ -5,7 +5,7 @@ import (
 )
 
 func TestReadLine(t *testing.T) {
-	//readLine() expects a line with two spaces.
+	// readLine() expects a line with two spaces.
 	good := "foo bar car"
 	foo, bar, car, err := readLine(good)
 	if err != nil {
@@ -28,7 +28,7 @@ func TestReadLine(t *testing.T) {
 
 func TestRun(t *testing.T) {
 	// NOTE(bacongobbler): not much we can test at this time other than it fails based on bad setup
-	if err := Run(nil, nil, nil, nil); err == nil {
-		t.Errorf("expected error to be non-nil, got nil")
+	if err := Run(nil, nil, nil); err != nil {
+		t.Errorf("expected error to be nil, got %s", err)
 	}
 }
diff --git a/pkg/healthsrv/buckets_lister.go b/pkg/healthsrv/buckets_lister.go
index cdb60ae..433d9d0 100644
--- a/pkg/healthsrv/buckets_lister.go
+++ b/pkg/healthsrv/buckets_lister.go
@@ -1,8 +1,8 @@
+// Package healthsrv provides health check services for the builder.
 package healthsrv
 
 import (
 	"context"
-	//"github.com/distribution/distribution/v3/context"
 )
 
 // BucketLister is a *(github.com/distribution/distribution/v3/registry/storage/driver).StorageDriver compatible interface that provides just
diff --git a/pkg/healthsrv/circuit_state.go b/pkg/healthsrv/circuit_state.go
index 208425c..9d3b3b8 100644
--- a/pkg/healthsrv/circuit_state.go
+++ b/pkg/healthsrv/circuit_state.go
@@ -7,7 +7,9 @@ import (
 )
 
 // circuitState determines whether circ.State() == sshd.ClosedState, and sends the results back on
-//  the various given channels. This func is intended to be run in a goroutine and communicates
+//
+//	the various given channels. This func is intended to be run in a goroutine and communicates
+//
 // via the channels it's passed.
 //
 // If the circuit is closed, it passes an empty struct back on succCh. On failure, it sends an
diff --git a/pkg/healthsrv/healthz_handler.go b/pkg/healthsrv/healthz_handler.go
index f6e7fcb..1c4be34 100644
--- a/pkg/healthsrv/healthz_handler.go
+++ b/pkg/healthsrv/healthz_handler.go
@@ -13,7 +13,7 @@ const (
 )
 
 func healthZHandler(bLister BucketLister, serverCircuit *sshd.Circuit) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+	return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
 		stopCh := make(chan struct{})
 
 		numChecks := 0
diff --git a/pkg/healthsrv/healthz_handler_test.go b/pkg/healthsrv/healthz_handler_test.go
index f94db3b..ce134de 100644
--- a/pkg/healthsrv/healthz_handler_test.go
+++ b/pkg/healthsrv/healthz_handler_test.go
@@ -5,26 +5,20 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
-	"strings"
 	"testing"
 
-	//"github.com/distribution/distribution/v3/context"
-
-	"github.com/arschles/assert"
 	"github.com/drycc/builder/pkg/sshd"
 	drycc "github.com/drycc/controller-sdk-go"
+	"github.com/stretchr/testify/assert"
 )
 
-var (
-	errTest = errors.New("test error")
-)
+var errTest = errors.New("test error")
 
 type emptyBucketLister struct{}
 
-func (e emptyBucketLister) List(ctx context.Context, opath string) ([]string, error) {
+func (e emptyBucketLister) List(context.Context, string) ([]string, error) {
 	return nil, nil
 }
 
@@ -32,34 +26,10 @@ type errBucketLister struct {
 	err error
 }
 
-func (e errBucketLister) List(ctx context.Context, opath string) ([]string, error) {
+func (e errBucketLister) List(context.Context, string) ([]string, error) {
 	return nil, e.err
 }
 
-type failureGetClient struct{}
-
-func (e failureGetClient) Get(url string) (*http.Response, error) {
-	resp := &http.Response{
-		Body:       ioutil.NopCloser(strings.NewReader("")),
-		StatusCode: http.StatusServiceUnavailable,
-	}
-	return resp, nil
-}
-
-//type emptyNamespaceLister struct{}
-//
-//func (n emptyNamespaceLister) List(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) {
-//	return &corev1.NamespaceList{}, nil
-//}
-//
-//type errNamespaceLister struct {
-//	err error
-//}
-//
-//func (e errNamespaceLister) List(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) {
-//	return nil, e.err
-//}
-
 type fakeHTTPServer struct {
 	// determines wether to return success or failure.
 	Healthy bool
@@ -90,7 +60,7 @@ func TestHealthZCircuitOpen(t *testing.T) {
 	h := healthZHandler(bLister, c)
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/healthz", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusServiceUnavailable, "response code")
 	assert.Equal(t, w.Body.Len(), 0, "response body length")
@@ -105,7 +75,7 @@ func TestHealthZBucketListErr(t *testing.T) {
 
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/healthz", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusServiceUnavailable, "response code")
 	assert.Equal(t, w.Body.Len(), 0, "response body length")
@@ -125,7 +95,7 @@ func TestReadinessNamespaceListErr(t *testing.T) {
 	h := readinessHandler(client, nsLister)
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/readiness", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusServiceUnavailable, "response code")
 	assert.Equal(t, w.Body.Len(), 0, "response body length")
@@ -145,7 +115,7 @@ func TestReadinessControllerErr(t *testing.T) {
 	h := readinessHandler(client, nsLister)
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/readiness", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusServiceUnavailable, "response code")
 }
@@ -158,7 +128,7 @@ func TestHealthZSuccess(t *testing.T) {
 	h := healthZHandler(bLister, c)
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/healthz", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusOK, "response code")
 	assert.Equal(t, w.Body.Len(), 0, "response body length")
@@ -178,7 +148,7 @@ func TestReadinessSuccess(t *testing.T) {
 	h := readinessHandler(client, nsLister)
 	w := httptest.NewRecorder()
 	r, err := http.NewRequest("GET", "/readiness", bytes.NewBuffer(nil))
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	h.ServeHTTP(w, r)
 	assert.Equal(t, w.Code, http.StatusOK, "response code")
 	assert.Equal(t, w.Body.Len(), 0, "response body length")
diff --git a/pkg/healthsrv/namespace_lister.go b/pkg/healthsrv/namespace_lister.go
index 7258d2b..2491e0c 100644
--- a/pkg/healthsrv/namespace_lister.go
+++ b/pkg/healthsrv/namespace_lister.go
@@ -2,6 +2,7 @@ package healthsrv
 
 import (
 	"context"
+
 	corev1 "k8s.io/api/core/v1"
 	//"k8s.io/apimachinery/pkg/labels"
 	//"k8s.io/apimachinery/pkg/fields"
@@ -19,7 +20,7 @@ type NamespaceLister interface {
 
 type emptyNamespaceLister struct{}
 
-func (n emptyNamespaceLister) List(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) {
+func (n emptyNamespaceLister) List(context.Context, metav1.ListOptions) (*corev1.NamespaceList, error) {
 	return &corev1.NamespaceList{}, nil
 }
 
@@ -27,7 +28,7 @@ type errNamespaceLister struct {
 	err error
 }
 
-func (e errNamespaceLister) List(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) {
+func (e errNamespaceLister) List(context.Context, metav1.ListOptions) (*corev1.NamespaceList, error) {
 	return nil, e.err
 }
 
diff --git a/pkg/healthsrv/readiness_handler.go b/pkg/healthsrv/readiness_handler.go
index b8ad390..3765a5b 100644
--- a/pkg/healthsrv/readiness_handler.go
+++ b/pkg/healthsrv/readiness_handler.go
@@ -6,11 +6,11 @@ import (
 	"time"
 
 	drycc "github.com/drycc/controller-sdk-go"
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 )
 
 func readinessHandler(client *drycc.Client, nsLister NamespaceLister) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+	return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
 		stopCh := make(chan struct{})
 
 		numChecks := 0
diff --git a/pkg/healthsrv/server.go b/pkg/healthsrv/server.go
index 56cc356..3697ff4 100644
--- a/pkg/healthsrv/server.go
+++ b/pkg/healthsrv/server.go
@@ -12,7 +12,7 @@ import (
 // with the indicative error.
 func Start(cnf *sshd.Config, nsLister NamespaceLister, bLister BucketLister, sshServerCircuit *sshd.Circuit) error {
 	mux := http.NewServeMux()
-	client, err := controller.New(cnf.ControllerHost, cnf.ControllerPort)
+	client, err := controller.New(cnf.ControllerURL)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go
index 8984e00..d69ac60 100644
--- a/pkg/k8s/client.go
+++ b/pkg/k8s/client.go
@@ -1,3 +1,4 @@
+// Package k8s provides Kubernetes client utilities for the builder service.
 package k8s
 
 import (
@@ -5,6 +6,7 @@ import (
 	"k8s.io/client-go/rest"
 )
 
+// NewInCluster creates a new kubernetes clientset using the in-cluster configuration.
 func NewInCluster() (*kubernetes.Clientset, error) {
 	// creates the in-cluster config
 	config, err := rest.InClusterConfig()
diff --git a/pkg/k8s/namespace.go b/pkg/k8s/namespace.go
index ca5c53b..2d53806 100644
--- a/pkg/k8s/namespace.go
+++ b/pkg/k8s/namespace.go
@@ -2,6 +2,7 @@ package k8s
 
 import (
 	"context"
+
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
diff --git a/pkg/k8s/secrets.go b/pkg/k8s/secrets.go
index a956ed3..a24c415 100644
--- a/pkg/k8s/secrets.go
+++ b/pkg/k8s/secrets.go
@@ -4,7 +4,6 @@ import (
 	"context"
 
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/watch"
@@ -17,52 +16,53 @@ import (
 // so you can unit test your code.
 type FakeSecret struct {
 	FnGet    func(string) (*corev1.Secret, error)
-	FnCreate func(*v1.Secret) (*corev1.Secret, error)
-	FnUpdate func(*v1.Secret) (*corev1.Secret, error)
+	FnCreate func(*corev1.Secret) (*corev1.Secret, error)
+	FnUpdate func(*corev1.Secret) (*corev1.Secret, error)
 }
 
 // Get is the interface definition.
-func (f *FakeSecret) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Secret, error) {
+func (f *FakeSecret) Get(_ context.Context, name string, _ metav1.GetOptions) (*corev1.Secret, error) {
 	return f.FnGet(name)
 }
 
 // Delete is the interface definition.
-func (f *FakeSecret) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
+func (f *FakeSecret) Delete(context.Context, string, metav1.DeleteOptions) error {
 	return nil
 }
 
 // Create is the interface definition.
-func (f *FakeSecret) Create(ctx context.Context, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
+func (f *FakeSecret) Create(_ context.Context, secret *corev1.Secret, _ metav1.CreateOptions) (*corev1.Secret, error) {
 	return f.FnCreate(secret)
 }
 
 // Update is the interface definition.
-func (f *FakeSecret) Update(ctx context.Context, secret *v1.Secret, opts metav1.UpdateOptions) (*v1.Secret, error) {
+func (f *FakeSecret) Update(_ context.Context, secret *corev1.Secret, _ metav1.UpdateOptions) (*corev1.Secret, error) {
 	return f.FnUpdate(secret)
 }
 
 // List is the interface definition.
-func (f *FakeSecret) List(ctx context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
-	return &v1.SecretList{}, nil
+func (f *FakeSecret) List(context.Context, metav1.ListOptions) (*corev1.SecretList, error) {
+	return &corev1.SecretList{}, nil
 }
 
 // Watch is the interface definition.
-func (f *FakeSecret) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
+func (f *FakeSecret) Watch(context.Context, metav1.ListOptions) (watch.Interface, error) {
 	return nil, nil
 }
 
 // DeleteCollection is the interface definition.
-func (f *FakeSecret) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
+func (f *FakeSecret) DeleteCollection(context.Context, metav1.DeleteOptions, metav1.ListOptions) error {
 	return nil
 }
 
 // Patch is the interface definition.
-func (f *FakeSecret) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Secret, error) {
-	return &v1.Secret{}, nil
+func (f *FakeSecret) Patch(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*corev1.Secret, error) {
+	return &corev1.Secret{}, nil
 }
 
-func (f *FakeSecret) Apply(ctx context.Context, secret *applyconfigurationscorev1.SecretApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Secret, err error) {
-	return &v1.Secret{}, nil
+// Apply is the interface definition for applying a secret configuration.
+func (f *FakeSecret) Apply(context.Context, *applyconfigurationscorev1.SecretApplyConfiguration, metav1.ApplyOptions) (result *corev1.Secret, err error) {
+	return &corev1.Secret{}, nil
 }
 
 // FakeSecretsNamespacer is a mock function that can be swapped in for an
@@ -71,6 +71,8 @@ func (f *FakeSecret) Apply(ctx context.Context, secret *applyconfigurationscorev
 //	Fn func(string) typedcorev1.SecretInterface
 //}
 
+// FakeSecretsGetter is a mock function that can be swapped in for an
+// (k8s.io/kubernetes/pkg/client/unversioned).SecretsNamespacer, so you can unit test you code
 type FakeSecretsGetter struct {
 	Fn func(string) typedcorev1.SecretInterface
 }
diff --git a/pkg/k8s/watch.go b/pkg/k8s/watch.go
index fb57da6..fe2ae47 100644
--- a/pkg/k8s/watch.go
+++ b/pkg/k8s/watch.go
@@ -2,30 +2,28 @@ package k8s
 
 import (
 	"context"
-	"k8s.io/api/core/v1"
+
+	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/watch"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/tools/cache"
-	"time"
-)
-
-var (
-	resyncPeriod = 30 * time.Second
 )
 
+// StoreToPodLister is a wrapper around cache.Store that provides methods to list pods.
 type StoreToPodLister struct {
 	cache.Store
 }
 
-//PodWatcher is a struct which holds the return values of (k8s.io/kubernetes/pkg/controller/framework).NewIndexerInformer together.
+// PodWatcher is a struct which holds the return values of (k8s.io/kubernetes/pkg/controller/framework).NewIndexerInformer together.
 type PodWatcher struct {
 	Store      StoreToPodLister
 	Controller cache.Controller
 }
 
+// List returns a list of pods that match the given label selector.
 func (s *StoreToPodLister) List(selector labels.Selector) (pods []*v1.Pod, err error) {
 	// TODO: it'd be great to just call
 	// s.Pods(api.NamespaceAll).List(selector), however then we'd have to
@@ -40,31 +38,29 @@ func (s *StoreToPodLister) List(selector labels.Selector) (pods []*v1.Pod, err e
 	return pods, nil
 }
 
-//NewPodWatcher creates a new BuildPodWatcher useful to list the pods using a cache which gets updated based on the watch func.
+// NewPodWatcher creates a new BuildPodWatcher useful to list the pods using a cache which gets updated based on the watch func.
 func NewPodWatcher(c kubernetes.Clientset, ns string) *PodWatcher {
 	pw := &PodWatcher{}
 
-	pw.Store.Store, pw.Controller = cache.NewIndexerInformer(
-		&cache.ListWatch{
+	pw.Store.Store, pw.Controller = cache.NewInformerWithOptions(cache.InformerOptions{
+		ListerWatcher: &cache.ListWatch{
 			ListFunc:  podListFunc(c, ns),
 			WatchFunc: podWatchFunc(c, ns),
 		},
-		&v1.Pod{},
-		resyncPeriod,
-		cache.ResourceEventHandlerFuncs{},
-		cache.Indexers{},
-	)
+		ObjectType: &v1.Pod{},
+		Handler:    cache.ResourceEventHandlerFuncs{},
+	})
 	return pw
 }
 
 func podListFunc(c kubernetes.Clientset, ns string) func(options metav1.ListOptions) (runtime.Object, error) {
-	return func(opts metav1.ListOptions) (runtime.Object, error) {
+	return func(metav1.ListOptions) (runtime.Object, error) {
 		return c.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
 	}
 }
 
 func podWatchFunc(c kubernetes.Clientset, ns string) func(options metav1.ListOptions) (watch.Interface, error) {
-	return func(opts metav1.ListOptions) (watch.Interface, error) {
+	return func(metav1.ListOptions) (watch.Interface, error) {
 		return c.CoreV1().Pods(ns).Watch(context.TODO(), metav1.ListOptions{})
 	}
 }
diff --git a/pkg/sshd/circuit.go b/pkg/sshd/circuit.go
index 0b63372..0d7069f 100644
--- a/pkg/sshd/circuit.go
+++ b/pkg/sshd/circuit.go
@@ -17,11 +17,12 @@ const (
 
 // String is the fmt.Stringer interface implementation.
 func (c CircuitState) String() string {
-	if c == OpenState {
+	switch c {
+	case OpenState:
 		return "OPEN"
-	} else if c == ClosedState {
+	case ClosedState:
 		return "CLOSED"
-	} else {
+	default:
 		return fmt.Sprintf("Unknown (%d)", c.toUint32())
 	}
 }
diff --git a/pkg/sshd/circuit_test.go b/pkg/sshd/circuit_test.go
index 0d6a71d..43aeb6f 100644
--- a/pkg/sshd/circuit_test.go
+++ b/pkg/sshd/circuit_test.go
@@ -5,7 +5,6 @@ import (
 	"sync"
 	"sync/atomic"
 	"testing"
-	"time"
 )
 
 const (
@@ -13,7 +12,9 @@ const (
 )
 
 func init() {
-	rand.Seed(time.Now().UnixNano())
+	// rand.Seed is deprecated as of Go 1.20
+	// Programs that call Seed with a known value should use New(NewSource(seed))
+	// No need to seed for random values as of Go 1.20
 }
 
 func TestOpenCloseSerial(t *testing.T) {
@@ -41,7 +42,7 @@ func TestOpenCloseConcurrent(t *testing.T) {
 	state := uint32(0)
 	for i := 0; i < numConcurrents; i++ {
 		wg.Add(1)
-		go func(i int) {
+		go func(int) {
 			defer wg.Done()
 			r := rand.Int() % 2
 			if r == 0 {
diff --git a/pkg/sshd/config.go b/pkg/sshd/config.go
index b85ebae..8d245df 100644
--- a/pkg/sshd/config.go
+++ b/pkg/sshd/config.go
@@ -6,14 +6,12 @@ import (
 
 // Config represents the required SSH server configuration.
 type Config struct {
-	ControllerHost              string `envconfig:"DRYCC_CONTROLLER_SERVICE_HOST" required:"true"`
-	ControllerPort              string `envconfig:"DRYCC_CONTROLLER_SERVICE_PORT" required:"true"`
+	ControllerURL               string `envconfig:"DRYCC_CONTROLLER_URL" required:"true"`
 	SSHHostIP                   string `envconfig:"SSH_HOST_IP" default:"0.0.0.0" required:"true"`
 	SSHHostPort                 int    `envconfig:"SSH_HOST_PORT" default:"2223" required:"true"`
 	HealthSrvPort               int    `envconfig:"HEALTH_SERVER_PORT" default:"8092"`
 	HealthSrvTestStorageRegion  string `envconfig:"STORAGE_REGION" default:"us-east-1"`
 	CleanerPollSleepDurationSec int    `envconfig:"CLEANER_POLL_SLEEP_DURATION_SEC" default:"5"`
-	StorageType                 string `envconfig:"BUILDER_STORAGE" default:"minio"`
 	ImagebuilderImagePullPolicy string `envconfig:"IMAGEBUILDER_IMAGE_PULL_POLICY" default:"Always"`
 	LockTimeout                 int    `envconfig:"GIT_LOCK_TIMEOUT" default:"10"`
 }
@@ -23,7 +21,7 @@ func (c Config) CleanerPollSleepDuration() time.Duration {
 	return time.Duration(c.CleanerPollSleepDurationSec) * time.Second
 }
 
-//GitLockTimeout return LockTimeout in minutes
+// GitLockTimeout return LockTimeout in minutes
 func (c Config) GitLockTimeout() time.Duration {
 	return time.Duration(c.LockTimeout) * time.Minute
 }
diff --git a/pkg/sshd/lock.go b/pkg/sshd/lock.go
index ea01107..9623013 100644
--- a/pkg/sshd/lock.go
+++ b/pkg/sshd/lock.go
@@ -7,9 +7,7 @@ import (
 	"time"
 )
 
-var (
-	errAlreadyLocked = errors.New("already locked")
-)
+var errAlreadyLocked = errors.New("already locked")
 
 // RepositoryLock interface that allows the creation of a lock associated
 // with a repository name to avoid simultaneous git operations.
diff --git a/pkg/sshd/lock_test.go b/pkg/sshd/lock_test.go
index 47e3323..8a5e416 100644
--- a/pkg/sshd/lock_test.go
+++ b/pkg/sshd/lock_test.go
@@ -6,23 +6,21 @@ import (
 	"testing"
 	"time"
 
-	"github.com/arschles/assert"
+	"github.com/stretchr/testify/assert"
 )
 
 const (
 	callbackTimeout = 1 * time.Second
 )
 
-var (
-	errGitReceive = errors.New("git receive error")
-)
+var errGitReceive = errors.New("git receive error")
 
 func TestMultipleSameRepoLocks(t *testing.T) {
 	var wg sync.WaitGroup
 	const repo = "repo1"
 	const numTries = 0
 	lck := NewInMemoryRepositoryLock(0)
-	assert.NoErr(t, lck.Lock(repo))
+	assert.Equal(t, lck.Lock(repo), nil)
 	for i := 0; i < numTries; i++ {
 		wg.Add(1)
 		go func() {
@@ -30,8 +28,8 @@ func TestMultipleSameRepoLocks(t *testing.T) {
 			assert.True(t, lck.Lock(repo) != nil, "lock of already locked repo should return error")
 		}()
 	}
-	assert.NoErr(t, waitWithTimeout(&wg, 1*time.Second))
-	assert.NoErr(t, lck.Unlock(repo))
+	assert.Equal(t, waitWithTimeout(&wg, 1*time.Second), nil)
+	assert.Equal(t, lck.Unlock(repo), nil)
 	for i := 0; i < numTries; i++ {
 		wg.Add(1)
 		go func() {
@@ -39,13 +37,13 @@ func TestMultipleSameRepoLocks(t *testing.T) {
 			assert.True(t, lck.Unlock(repo) != nil, "unlock of already unlocked repo should return error")
 		}()
 	}
-	assert.NoErr(t, waitWithTimeout(&wg, 1*time.Second))
+	assert.Equal(t, waitWithTimeout(&wg, 1*time.Second), nil)
 }
 
 func TestSingleLock(t *testing.T) {
 	rl := NewInMemoryRepositoryLock(0)
 	key := "fakeid"
-	callbackCh := make(chan interface{})
+	callbackCh := make(chan any)
 	go lockAndCallback(rl, key, callbackCh)
 	verifyCallbackHappens(t, callbackCh)
 }
@@ -53,7 +51,7 @@ func TestSingleLock(t *testing.T) {
 func TestSingleLockUnlock(t *testing.T) {
 	rl := NewInMemoryRepositoryLock(0)
 	key := "fakeid"
-	callbackCh := make(chan interface{})
+	callbackCh := make(chan any)
 	go lockAndCallback(rl, key, callbackCh)
 	verifyCallbackHappens(t, callbackCh)
 	err := rl.Unlock(key)
@@ -74,8 +72,8 @@ func TestInvalidUnlock(t *testing.T) {
 func TestDoubleLockUnlock(t *testing.T) {
 	rl := NewInMemoryRepositoryLock(0)
 	key := "fakeid"
-	callbackCh1stLock := make(chan interface{})
-	callbackCh2ndLock := make(chan interface{})
+	callbackCh1stLock := make(chan any)
+	callbackCh2ndLock := make(chan any)
 
 	go lockAndCallback(rl, key, callbackCh1stLock)
 	verifyCallbackHappens(t, callbackCh1stLock)
@@ -94,29 +92,29 @@ func TestDoubleLockUnlock(t *testing.T) {
 func TestWrapInLock(t *testing.T) {
 	const repoName = "repo"
 	lck := NewInMemoryRepositoryLock(100 * time.Second)
-	assert.NoErr(t, wrapInLock(lck, repoName, func() error {
+	assert.Equal(t, wrapInLock(lck, repoName, func() error {
 		return nil
-	}))
-	assert.NoErr(t, lck.Lock(repoName))
-	assert.Err(t, errAlreadyLocked, wrapInLock(lck, repoName, func() error {
+	}), nil)
+	assert.Equal(t, lck.Lock(repoName), nil)
+	assert.Error(t, errAlreadyLocked, wrapInLock(lck, repoName, func() error {
 		return errGitReceive
 	}))
-	assert.Err(t, errAlreadyLocked, wrapInLock(lck, repoName, func() error {
+	assert.Error(t, errAlreadyLocked, wrapInLock(lck, repoName, func() error {
 		return nil
 	}))
-	assert.NoErr(t, lck.Unlock(repoName))
-	assert.NoErr(t, wrapInLock(lck, repoName, func() error {
+	assert.Equal(t, lck.Unlock(repoName), nil)
+	assert.Equal(t, wrapInLock(lck, repoName, func() error {
 		return nil
-	}))
+	}), nil)
 }
 
-func lockAndCallback(rl RepositoryLock, id string, callbackCh chan<- interface{}) {
+func lockAndCallback(rl RepositoryLock, id string, callbackCh chan<- any) {
 	if err := rl.Lock(id); err == nil {
 		callbackCh <- true
 	}
 }
 
-func verifyCallbackHappens(t *testing.T, callbackCh <-chan interface{}) bool {
+func verifyCallbackHappens(t *testing.T, callbackCh <-chan any) bool {
 	select {
 	case <-callbackCh:
 		return true
@@ -126,7 +124,7 @@ func verifyCallbackHappens(t *testing.T, callbackCh <-chan interface{}) bool {
 	}
 }
 
-func verifyCallbackDoesntHappens(t *testing.T, callbackCh <-chan interface{}) bool {
+func verifyCallbackDoesntHappens(t *testing.T, callbackCh <-chan any) bool {
 	select {
 	case <-callbackCh:
 		t.Fatalf("Unexpected callback.")
diff --git a/pkg/sshd/server.go b/pkg/sshd/server.go
index a7440e8..67e45cf 100644
--- a/pkg/sshd/server.go
+++ b/pkg/sshd/server.go
@@ -7,8 +7,8 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
+	"os"
 	"strings"
 
 	"github.com/drycc/builder/pkg/controller"
@@ -29,14 +29,16 @@ const (
 	multiplePush string = "Another git push is ongoing"
 )
 
-var errBuildAppPerm = errors.New("user has no permission to build the app")
-var errDirPerm = errors.New("cannot change directory in file name")
-var errDirCreatePerm = errors.New("empty repo name")
+var (
+	errBuildAppPerm  = errors.New("user has no permission to build the app")
+	errDirPerm       = errors.New("cannot change directory in file name")
+	errDirCreatePerm = errors.New("empty repo name")
+)
 
 // AuthKey authenticates based on a public key.
 func AuthKey(key ssh.PublicKey, cnf *Config) (*ssh.Permissions, error) {
 	log.Info("Starting ssh authentication")
-	client, err := controller.New(cnf.ControllerHost, cnf.ControllerPort)
+	client, err := controller.New(cnf.ControllerURL)
 	if err != nil {
 		return nil, err
 	}
@@ -71,10 +73,11 @@ func AuthKey(key ssh.PublicKey, cnf *Config) (*ssh.Permissions, error) {
 // ConfigureServerSshConfig
 //
 // Returns:
-//  An *ssh.ServerConfig
+//
+//	An *ssh.ServerConfig
 func Configure(cnf *Config) (*ssh.ServerConfig, error) {
 	cfg := &ssh.ServerConfig{
-		PublicKeyCallback: func(m ssh.ConnMetadata, k ssh.PublicKey) (*ssh.Permissions, error) {
+		PublicKeyCallback: func(_ ssh.ConnMetadata, k ssh.PublicKey) (*ssh.Permissions, error) {
 			return AuthKey(k, cnf)
 		},
 	}
@@ -83,7 +86,7 @@ func Configure(cnf *Config) (*ssh.ServerConfig, error) {
 	for _, t := range hostKeyTypes {
 		path := fmt.Sprintf(pathTpl, t)
 
-		key, err := ioutil.ReadFile(path)
+		key, err := os.ReadFile(path)
 		if err != nil {
 			log.Debug("Failed to read key %s (skipping): %s", path, err)
 			return nil, err
@@ -96,7 +99,7 @@ func Configure(cnf *Config) (*ssh.ServerConfig, error) {
 		log.Debug("Parsed host key %s.", path)
 		cfg.AddHostKey(hk)
 	}
-	cfg.Config.Ciphers = []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com"}
+	cfg.Ciphers = []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com"}
 	return cfg, nil
 }
 
@@ -106,8 +109,8 @@ func Serve(
 	serverCircuit *Circuit,
 	gitHomeDir string,
 	concurrentPushLock RepositoryLock,
-	addr, receivetype string) error {
-
+	addr, receivetype string,
+) error {
 	listener, err := net.Listen("tcp", addr)
 	if err != nil {
 		return err
@@ -136,7 +139,6 @@ type server struct {
 // listen handles accepting and managing connections. However, since closer
 // is len(1), it will not block the sender.
 func (s *server) listen(l net.Listener, conf *ssh.ServerConfig) error {
-
 	log.Info("Accepting new connections.")
 	defer l.Close()
 
@@ -199,7 +201,7 @@ func sshConnection(conn net.Conn) string {
 }
 
 func sendExitStatus(status uint32, channel ssh.Channel) error {
-	exit := struct{ Status uint32 }{uint32(0)}
+	exit := struct{ Status uint32 }{uint32(status)}
 	_, err := channel.SendRequest("exit-status", false, ssh.Marshal(exit))
 	return err
 }
@@ -347,9 +349,8 @@ func cleanExec(pay []byte) string {
 // Returns the string PONG and exit status 0.
 //
 // Params:
-// 	- channel (ssh.Channel): The channel to respond on.
-// 	- request (*ssh.Request): The request.
-//
+//   - channel (ssh.Channel): The channel to respond on.
+//   - request (*ssh.Request): The request.
 func Ping(channel ssh.Channel, req *ssh.Request) error {
 	log.Info("PING")
 	if _, err := channel.Write([]byte("pong")); err != nil {
@@ -368,7 +369,7 @@ func cleanRepoName(name string) (string, error) {
 	if strings.Contains(name, "..") {
 		return "", errDirPerm
 	}
-	name = strings.Replace(name, "'", "", -1)
+	name = strings.ReplaceAll(name, "'", "")
 	return strings.TrimPrefix(strings.TrimSuffix(name, ".git"), "/"), nil
 }
 
diff --git a/pkg/sshd/server_test.go b/pkg/sshd/server_test.go
index 1e2c5be..0d88827 100644
--- a/pkg/sshd/server_test.go
+++ b/pkg/sshd/server_test.go
@@ -8,7 +8,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/arschles/assert"
+	"github.com/stretchr/testify/assert"
 	"golang.org/x/crypto/ssh"
 )
 
@@ -20,7 +20,7 @@ func TestGitPktLine(t *testing.T) {
 	b := new(bytes.Buffer)
 	str := "hello world"
 	err := gitPktLine(b, str)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	outStr := b.String()
 	assert.True(t, len(outStr) > 4, "output string <= 4 chars")
@@ -30,7 +30,7 @@ func TestGitPktLine(t *testing.T) {
 
 func serverConfigure() (*ssh.ServerConfig, error) {
 	cfg := &ssh.ServerConfig{
-		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
+		PasswordCallback: func(ssh.ConnMetadata, []byte) (*ssh.Permissions, error) {
 			return mockAuthKey()
 		},
 	}
@@ -43,7 +43,7 @@ func clientConfig() *ssh.ClientConfig {
 		Auth: []ssh.AuthMethod{
 			ssh.Password("password"),
 		},
-		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+		HostKeyCallback: func(string, net.Addr, ssh.PublicKey) error {
 			return nil
 		},
 	}
@@ -59,10 +59,10 @@ func clientConfig() *ssh.ClientConfig {
 func TestReceive(t *testing.T) {
 	const testingServerAddr = "127.0.0.1:2244"
 	key, err := sshTestingHostKey()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	cfg, err := serverConfigure()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	cfg.AddHostKey(key)
 
 	c := NewCircuit()
@@ -106,17 +106,16 @@ func TestReceive(t *testing.T) {
 	if err := sess.Run("illegal command"); err == nil {
 		t.Fatalf("expected a failed run with command 'illegal command'")
 	}
-
 }
 
 // TestPushInvalidArgsLength tests trying to do a push with only the command, not the repo
 func TestPushInvalidArgsLength(t *testing.T) {
 	const testingServerAddr = "127.0.0.1:2252"
 	key, err := sshTestingHostKey()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	cfg, err := serverConfigure()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	cfg.AddHostKey(key)
 
 	c := NewCircuit()
@@ -130,11 +129,11 @@ func TestPushInvalidArgsLength(t *testing.T) {
 
 	// Connect to the server and issue env var set. This should return true.
 	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	// check for invalid length of arguments
 	sess, err := client.NewSession()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	defer sess.Close()
 	if out, err := sess.Output("git-upload-pack"); err == nil {
 		t.Errorf("Expected an error but '%s' was received", out)
@@ -147,10 +146,10 @@ func TestPushInvalidArgsLength(t *testing.T) {
 func TestConcurrentPushSameRepo(t *testing.T) {
 	const testingServerAddr = "127.0.0.1:2245"
 	key, err := sshTestingHostKey()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	cfg, err := serverConfigure()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	cfg.AddHostKey(key)
 
 	c := NewCircuit()
@@ -164,14 +163,14 @@ func TestConcurrentPushSameRepo(t *testing.T) {
 
 	// Connect to the server and issue env var set. This should return true.
 	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	const numPushers = 4
 	outCh := make(chan *sshSessionOutput, numPushers)
 	for i := 0; i < numPushers; i++ {
 		go func() {
 			sess, newSessErr := client.NewSession()
-			assert.NoErr(t, newSessErr)
+			assert.Equal(t, newSessErr, nil)
 			defer sess.Close()
 			out, outErr := sess.Output("git-upload-pack /demo.git")
 			outCh <- &sshSessionOutput{outStr: string(out), err: outErr}
@@ -202,7 +201,7 @@ func TestConcurrentPushDifferentRepo(t *testing.T) {
 		t.Fatal(err)
 	}
 	cfg, err := serverConfigure()
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	cfg.AddHostKey(key)
 	c := NewCircuit()
 	pushLock := NewInMemoryRepositoryLock(time.Duration(1 * time.Minute))
@@ -212,7 +211,7 @@ func TestConcurrentPushDifferentRepo(t *testing.T) {
 
 	// Connect to the server and issue env var set. This should return true.
 	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 
 	const numRepos = 3
 	repoNames := make([]string, numRepos)
@@ -225,14 +224,14 @@ func TestConcurrentPushDifferentRepo(t *testing.T) {
 		go func(repoName string) {
 			defer wg.Done()
 			sess, err := client.NewSession()
-			assert.NoErr(t, err)
+			assert.Equal(t, err, nil)
 			out, err := sess.Output("git-upload-pack /" + repoName + ".git")
-			assert.NoErr(t, err)
+			assert.Equal(t, err, nil)
 			assert.Equal(t, string(out), "OK", "output")
 		}(repoName)
 	}
 	wg.Wait()
-	assert.NoErr(t, waitWithTimeout(&wg, 1*time.Second))
+	assert.Equal(t, waitWithTimeout(&wg, 1*time.Second), nil)
 }
 
 // sshTestingHostKey loads the testing key.
@@ -245,9 +244,9 @@ func runServer(
 	c *Circuit,
 	pushLock RepositoryLock,
 	testAddr string,
-	handlerSleepDur time.Duration,
-	t *testing.T) {
-
+	_ time.Duration,
+	t *testing.T,
+) {
 	go func() {
 		if err := Serve(config, c, gitHome, pushLock, testAddr, "mock"); err != nil {
 			t.Errorf("Failed serving with %s", err)
@@ -266,25 +265,7 @@ func mockAuthKey() (*ssh.Permissions, error) {
 	return perm, nil
 }
 
-// connMetadata mocks ssh.ConnMetadata for authentication.
-type connMetadata struct{}
-
-func (cm *connMetadata) User() string          { return "drycc" }
-func (cm *connMetadata) SessionID() []byte     { return []byte("1") }
-func (cm *connMetadata) ClientVersion() []byte { return []byte("2.3.4") }
-func (cm *connMetadata) ServerVersion() []byte { return []byte("2.3.4") }
-func (cm *connMetadata) RemoteAddr() net.Addr  { return cm.localhost() }
-func (cm *connMetadata) LocalAddr() net.Addr   { return cm.localhost() }
-func (cm *connMetadata) localhost() net.Addr {
-	addrs, err := net.InterfaceAddrs()
-	if err != nil {
-		panic(err)
-	}
-	return addrs[0]
-}
-
-var (
-	testingHostKey = `-----BEGIN RSA PRIVATE KEY-----
+var testingHostKey = `-----BEGIN RSA PRIVATE KEY-----
 MIIEpAIBAAKCAQEA0xOK/wubqj+e4HNp+yAdK4WJnLZCvcjS2DwaxwF+E968kSeU
 27SOqiol7Y0UwLGLpB6rpIBnSqXo70xiMUSrnteKmMejddzfbGkvnyvo0dwE4nDd
 vnbz64I25xfjTldb4RtNvpk6ymr0soq0EEYssLmdnt7pIgHT71n9RNtu+RPpRe5n
@@ -312,4 +293,3 @@ v3+ZZfZMlci4pxBtXqrnoyj4uUoqZtR3ENLz53SN1i0vpT7DtC6gMnEF1UWiaoJ6
 6mGH5/bxCg9wpV7qpqR0EbFM/dhQFZmmnirOS8x+00hJvc1HFiuN/A==
 -----END RSA PRIVATE KEY-----
 `
-)
diff --git a/pkg/storage/image_repo.go b/pkg/storage/image_repo.go
index a4dd0cd..3af3198 100644
--- a/pkg/storage/image_repo.go
+++ b/pkg/storage/image_repo.go
@@ -1,3 +1,4 @@
+// Package storage provides image repository management functionality.
 package storage
 
 import (
@@ -32,7 +33,7 @@ func CreateImageRepo(reponame string, params map[string]string) error {
 	}
 	regionName, ok = params["region"]
 	if !ok || fmt.Sprint(regionName) == "" {
-		return fmt.Errorf("No region parameter provided")
+		return fmt.Errorf("no region parameter provided: %s", regionName)
 	}
 	region := fmt.Sprint(regionName)
 	creds := credentials.NewChainCredentials([]credentials.Provider{
@@ -44,19 +45,20 @@ func CreateImageRepo(reponame string, params map[string]string) error {
 		},
 		&credentials.EnvProvider{},
 		&credentials.SharedCredentialsProvider{},
-		&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
+		&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.Must(session.NewSession()))},
 	})
 	awsConfig := aws.NewConfig()
 	awsConfig.WithCredentials(creds)
 	awsConfig.WithRegion(region)
-	svc := ecr.New(session.New(awsConfig))
 
+	session, err := session.NewSession(awsConfig)
+	if err != nil {
+		return err
+	}
 	repoInput := &ecr.CreateRepositoryInput{
 		RepositoryName: aws.String(reponame),
 	}
-
-	_, err := svc.CreateRepository(repoInput)
-	if err != nil {
+	if _, err := ecr.New(session).CreateRepository(repoInput); err != nil {
 		if s3Err, ok := err.(awserr.Error); ok && s3Err.Code() == "RepositoryAlreadyExistsException" {
 			return nil
 		}
diff --git a/pkg/storage/interfaces.go b/pkg/storage/interfaces.go
index 09791e9..70b553f 100644
--- a/pkg/storage/interfaces.go
+++ b/pkg/storage/interfaces.go
@@ -2,7 +2,7 @@ package storage
 
 import (
 	"context"
-	//"github.com/distribution/distribution/v3/context"
+
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
 )
 
diff --git a/pkg/storage/object.go b/pkg/storage/object.go
index be57837..12cac7e 100644
--- a/pkg/storage/object.go
+++ b/pkg/storage/object.go
@@ -1,11 +1,11 @@
 package storage
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"time"
 
-	"github.com/distribution/distribution/v3/context"
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
 )
 
@@ -39,9 +39,8 @@ func WaitForObject(statter ObjectStatter, objKey string, tick, timeout time.Dura
 			return err
 		} else if exists {
 			return nil
-		} else {
-			return noExist
 		}
+		return noExist
 	}
 	if err := checker(); err == nil {
 		return nil
@@ -60,7 +59,7 @@ func WaitForObject(statter ObjectStatter, objKey string, tick, timeout time.Dura
 			if err := checker(); err == nil {
 				return nil
 			}
-			return fmt.Errorf("Object %s didn't exist after %s", objKey, timeout)
+			return fmt.Errorf("object %s didn't exist after %s", objKey, timeout)
 		}
 	}
 }
diff --git a/pkg/storage/object_test.go b/pkg/storage/object_test.go
index 8c580e5..acd35ff 100644
--- a/pkg/storage/object_test.go
+++ b/pkg/storage/object_test.go
@@ -6,10 +6,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/arschles/assert"
-
-	//"github.com/distribution/distribution/v3/context"
 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
+	"github.com/stretchr/testify/assert"
 )
 
 const (
@@ -24,7 +22,7 @@ func TestObjectExistsSuccess(t *testing.T) {
 		},
 	}
 	exists, err := ObjectExists(statter, objPath)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	assert.True(t, exists, "object not found when it should be present")
 	assert.Equal(t, len(statter.Calls), 1, "number of StatObject calls")
 	assert.Equal(t, statter.Calls[0].Path, objPath, "object key")
@@ -37,7 +35,7 @@ func TestObjectExistsNoObject(t *testing.T) {
 		},
 	}
 	exists, err := ObjectExists(statter, objPath)
-	assert.NoErr(t, err)
+	assert.Equal(t, err, nil)
 	assert.False(t, exists, "object found when it should be missing")
 	assert.Equal(t, len(statter.Calls), 1, "number of StatObject calls")
 }
@@ -50,7 +48,7 @@ func TestObjectExistsOtherErr(t *testing.T) {
 		},
 	}
 	exists, err := ObjectExists(statter, objPath)
-	assert.Err(t, err, expectedErr)
+	assert.Error(t, err, expectedErr)
 	assert.False(t, exists, "object found when the statter errored")
 }
 
@@ -76,7 +74,7 @@ func TestWaitForObjectExists(t *testing.T) {
 			return storagedriver.FileInfoInternal{FileInfoFields: storagedriver.FileInfoFields{}}, nil
 		},
 	}
-	assert.NoErr(t, WaitForObject(statter, objPath, 1*time.Millisecond, 2*time.Millisecond))
+	assert.Equal(t, WaitForObject(statter, objPath, 1*time.Millisecond, 2*time.Millisecond), nil)
 	// it should make 1 call immediately, then immediateley succeed
 	assert.Equal(t, len(statter.Calls), 1, "number of calls to the statter")
 }
diff --git a/pkg/sys/env.go b/pkg/sys/env.go
index 6e2b120..568bbb9 100644
--- a/pkg/sys/env.go
+++ b/pkg/sys/env.go
@@ -1,13 +1,16 @@
+// Package sys provides system utilities for environment variable handling.
 package sys
 
 import (
 	"os"
+	"strings"
 )
 
 // Env is an interface to a set of environment variables.
 type Env interface {
 	// Get gets the environment variable of the given name.
 	Get(name string) string
+	Environ(prefixs []string) map[string]string
 }
 
 type realEnv struct{}
@@ -16,6 +19,19 @@ func (r realEnv) Get(name string) string {
 	return os.Getenv(name)
 }
 
+func (r realEnv) Environ(prefixs []string) map[string]string {
+	envs := make(map[string]string)
+	for _, env := range os.Environ() {
+		pair := strings.SplitN(env, "=", 2)
+		for index := range prefixs {
+			if _, hasKey := envs[pair[0]]; !hasKey && strings.HasPrefix(pair[0], prefixs[index]) {
+				envs[pair[0]] = pair[1]
+			}
+		}
+	}
+	return envs
+}
+
 // RealEnv returns an Env implementation that uses os.Getenv every time Get is called.
 func RealEnv() Env {
 	return realEnv{}
@@ -36,3 +52,16 @@ func NewFakeEnv() *FakeEnv {
 func (f *FakeEnv) Get(name string) string {
 	return f.Envs[name]
 }
+
+// Environ returns a map of environment variables that match the given prefixes.
+func (f *FakeEnv) Environ(prefixs []string) map[string]string {
+	envs := make(map[string]string)
+	for key := range f.Envs {
+		for index := range prefixs {
+			if _, hasKey := envs[key]; !hasKey && strings.HasPrefix(key, prefixs[index]) {
+				envs[key] = f.Envs[key]
+			}
+		}
+	}
+	return envs
+}
diff --git a/pkg/sys/fs.go b/pkg/sys/fs.go
index 3d09f46..24b35fa 100644
--- a/pkg/sys/fs.go
+++ b/pkg/sys/fs.go
@@ -2,7 +2,6 @@ package sys
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 )
 
@@ -26,7 +25,7 @@ type realFS struct{}
 
 // ReadFile is the interface implementation for FS.
 func (r *realFS) ReadFile(name string) ([]byte, error) {
-	return ioutil.ReadFile(name)
+	return os.ReadFile(name)
 }
 
 // RemoveAll is the interface implementation for FS.
diff --git a/pkg/sys/fs_test.go b/pkg/sys/fs_test.go
index 5a6a35c..730a2b1 100644
--- a/pkg/sys/fs_test.go
+++ b/pkg/sys/fs_test.go
@@ -2,7 +2,6 @@ package sys
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"testing"
 )
@@ -18,7 +17,7 @@ func TestRealFS(t *testing.T) {
 		t.Error("expected to receive error when real file does not exist")
 	}
 
-	tmpfile, err := ioutil.TempFile("", testFilename)
+	tmpfile, err := os.CreateTemp("", testFilename)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/rootfs/README.md b/rootfs/README.md
index 42e6ceb..87d36ba 100644
--- a/rootfs/README.md
+++ b/rootfs/README.md
@@ -1,8 +1,8 @@
 # RootFS
 
 This directory stores all files that should be copied to the rootfs of a
-Docker container. The files should be stored according to the correct
-directory structure of the destination container. For example:
+container. The files should be stored according to the correct directory
+structure of the destination container. For example:
 
 ```
 rootfs/bin -> /bin
diff --git a/rootfs/bin/create_bucket b/rootfs/bin/create_bucket
index 196a744..addadaa 100644
--- a/rootfs/bin/create_bucket
+++ b/rootfs/bin/create_bucket
@@ -2,18 +2,18 @@
 
 set -e
 
-# shellcheck disable=SC1091
-source /bin/normalize_storage
+mkdir -p ~/.config/rclone
+touch ~/.config/rclone/rclone.conf
+rclone config create storage s3 \
+  provider=Other \
+  access_key_id="${DRYCC_STORAGE_ACCESSKEY}" \
+  secret_access_key="${DRYCC_STORAGE_SECRETKEY}" \
+  endpoint="${DRYCC_STORAGE_ENDPOINT}" \
+  force_path_style="${DRYCC_STORAGE_PATH_STYLE:-true}" --no-output
 
-has_bucket(){
-    mc ls minio -json|jq -r '.key'|grep -w "${MINIO_BUCKET}"
-}
-
-if  [ -z "$(has_bucket)" ] ;then
-    mc mb minio/"${MINIO_BUCKET}"
-    if  [ -z "$(has_bucket)" ] ;then
-        echo "create bucket ${MINIO_BUCKET} error"
-        exit 1
-    fi
+if ! rclone lsd storage: > /dev/null 2>&1; then
+  sleep 9s
+  echo "waiting for object storage to become ready..."
 fi
-echo "create bucket ${MINIO_BUCKET} success"
+
+rclone mkdir "storage:${DRYCC_STORAGE_BUCKET}"
diff --git a/rootfs/bin/normalize_storage b/rootfs/bin/normalize_storage
deleted file mode 100644
index c1a0b33..0000000
--- a/rootfs/bin/normalize_storage
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-BUCKET_FILE="/var/run/secrets/drycc/objectstore/creds/builder-bucket"
-ACCESS_KEY_FILE="/var/run/secrets/drycc/objectstore/creds/accesskey"
-SECRET_KEY_FILE="/var/run/secrets/drycc/objectstore/creds/secretkey"
-
-if [ -f $BUCKET_FILE ]; then
-  MINIO_BUCKET=$(cat "$BUCKET_FILE")
-  export MINIO_BUCKET
-fi
-if [ -f $ACCESS_KEY_FILE ]; then
-  MINIO_ACCESS_KEY=$(cat "$ACCESS_KEY_FILE")
-  export MINIO_ACCESS_KEY
-fi
-if [ -f $SECRET_KEY_FILE ]; then
-  MINIO_SECRET_KEY=$(cat "$SECRET_KEY_FILE")
-  export MINIO_SECRET_KEY
-fi
-
-export MINIO_ENDPOINT=http://"${DRYCC_MINIO_SERVICE_HOST}:${DRYCC_MINIO_SERVICE_PORT}"
-
-mc config host add minio "${MINIO_ENDPOINT}" "${MINIO_ACCESS_KEY}" "${MINIO_SECRET_KEY}"
diff --git a/rootfs/docker-entrypoint.sh b/rootfs/container-entrypoint.sh
similarity index 100%
rename from rootfs/docker-entrypoint.sh
rename to rootfs/container-entrypoint.sh
diff --git a/testdata/bin/fake-k8s b/testdata/bin/fake-k8s
new file mode 100755
index 0000000..bd50573
--- /dev/null
+++ b/testdata/bin/fake-k8s
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+#
+# This script is designed to be run inside the container
+#
+
+# fail hard and fast even on pipelines
+set -eou pipefail
+
+function fake_k8s_env() {
+    export KUBERNETES_SERVICE_HOST=10.43.0.1
+    export KUBERNETES_SERVICE_PORT=443
+    mkdir -p /var/run/secrets/kubernetes.io/serviceaccount
+    cat << EOF > "/var/run/secrets/kubernetes.io/serviceaccount/token"
+eyJhbGciOiJSUzI1NiIsImtpZCI6IlBrb1V6eGNRUWFfSEU3NG1lLWxZaUlnMUhEYnhnYWc1RXo2ZC1wOW4yMncifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxNzE4NzU1OTY2LCJpYXQiOjE2ODcyMTk5NjYsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkcnljYyIsInBvZCI6eyJuYW1lIjoiZHJ5Y2MtZGF0YWJhc2UtMiIsInVpZCI6IjljMDEzYjcwLTgzNmUtNDI2NC05OTMyLWM1NWY3ZTg2ODQyOSJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZHJ5Y2MtZGF0YWJhc2UiLCJ1aWQiOiI4MWVhNjM4ZC1jYjZhLTQ3YzYtYWRjMi1iODgzM2Q3NjMwNzYifSwid2FybmFmdGVyIjoxNjg3MjIzNTczfSwibmJmIjoxNjg3MjE5OTY2LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZHJ5Y2M6ZHJ5Y2MtZGF0YWJhc2UifQ.fmSqpvSPHB9KUD1preXfp5MPE4WQrxDppFVD5EPyH_RskYRtrP8z4-ys0kaRcqf4tlsMovnBFgv9dvHWJGBxlowzXWWaaXwxEMCwwIjlLDEyTn2hazdmYINfB61xjvOq0EAUeZP3GKd_nPFqmCM21bs9mzvWQRAPCsis60o_DISbPaipxea-An1WPLysJH_saByx1qOCaAfwSROp8lUKX5db_-mpr2lAj-130UelVxa0umJ0BY0HA6jfeBLo_vRH4Y7-enKeY0Zce93zjYmZHgL7Lep3K4w0ja9uECiuIhcOyxYz-SYe1Hym4OSCuqJEP9kiyZ9B3irIDcyRCcRpKw
+EOF
+    cat << EOF > "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
+-----BEGIN CERTIFICATE-----
+MIIBdzCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
+dmVyLWNhQDE2ODU5MzE2NzcwHhcNMjMwNjA1MDIyMTE3WhcNMzMwNjAyMDIyMTE3
+WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE2ODU5MzE2NzcwWTATBgcqhkjO
+PQIBBggqhkjOPQMBBwNCAAR4ULHC4BX7Ss8BVBrrdcT7o57tc323jlLczUOxVQVo
+Q4LkoXY1EAKrjEaFu1HGo3Gr8GQ6d/ZJNzrTAWTGIo0ro0IwQDAOBgNVHQ8BAf8E
+BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUnPRP/O6bnrUeVknNFFUe
+Iw4ruF8wCgYIKoZIzj0EAwIDSAAwRQIgHjUAelTQCKoehiZM8c+RYyeJGKvjTB44
+W5RbH+np4T0CIQDCgnhCCPAWtS04DRR7nFog3ilH9bFoIj0g5tOTEVNfLg==
+-----END CERTIFICATE-----
+EOF
+}
+
+. init-stack
+fake_k8s_env
+
+"$@"
diff --git a/versioning.mk b/versioning.mk
index 2bfd999..8b30958 100644
--- a/versioning.mk
+++ b/versioning.mk
@@ -11,13 +11,13 @@ info:
 	@echo "Immutable tag:   ${IMAGE}"
 	@echo "Mutable tag:     ${MUTABLE_IMAGE}"
 
-.PHONY: docker-push
-docker-push: docker-immutable-push docker-mutable-push
+.PHONY: podman-push
+podman-push: podman-immutable-push podman-mutable-push
 
-.PHONY: docker-immutable-push
-docker-immutable-push:
-	docker push ${IMAGE}
+.PHONY: podman-immutable-push
+podman-immutable-push:
+	podman push ${IMAGE}
 
-.PHONY: docker-mutable-push
-docker-mutable-push:
-	docker push ${MUTABLE_IMAGE}
+.PHONY: podman-mutable-push
+podman-mutable-push:
+	podman push ${MUTABLE_IMAGE}
