From 39f498a4268d078b52aadde45d3be34adfdb92ff Mon Sep 17 00:00:00 2001 From: Benny Zlotnik Date: Mon, 10 Aug 2026 09:23:59 +0300 Subject: [PATCH 1/3] perf(ci): parallelize e2e operator build prerequisites docker-build, docker-build-exporter-set-controller, build-operator, and cluster creation are independent targets. Running with -j3 lets them overlap, saving ~2-3 minutes of sequential container builds. Signed-off-by: Benny Zlotnik --- .github/workflows/controller-kind.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/controller-kind.yaml b/.github/workflows/controller-kind.yaml index d5bfff854..2a9868501 100644 --- a/.github/workflows/controller-kind.yaml +++ b/.github/workflows/controller-kind.yaml @@ -29,4 +29,4 @@ jobs: - name: Run operator e2e test working-directory: controller - run: make test-operator-e2e + run: make test-operator-e2e -j3 --output-sync=target From 5d60ceaa54e70fcadc7baddca742e9506767133f Mon Sep 17 00:00:00 2001 From: Benny Zlotnik Date: Mon, 10 Aug 2026 09:31:09 +0300 Subject: [PATCH 2/3] perf(ci): compile Go on host with cached modules instead of inside containers Adds a CI-optimized build path that: - Compiles Go binaries directly on the runner (with setup-go module cache) - Packages them into minimal runtime-only containers (no Go toolchain) - Eliminates pulling the ~1.5GB go-toolset image for each build - Eliminates re-downloading modules inside containers without cache New targets: docker-build-ci, build-operator-ci, deploy-operator-ci, test-operator-e2e-ci. The CI workflow now uses setup-go with cache and the -ci targets. Signed-off-by: Benny Zlotnik --- .github/workflows/controller-kind.yaml | 8 +++++++- controller/Containerfile.prebuilt | 13 +++++++++++++ controller/Makefile | 27 +++++++++++++++++++++++++- controller/deploy/operator/Makefile | 8 ++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 controller/Containerfile.prebuilt diff --git a/.github/workflows/controller-kind.yaml b/.github/workflows/controller-kind.yaml index 2a9868501..91ec1a2ea 100644 --- a/.github/workflows/controller-kind.yaml +++ b/.github/workflows/controller-kind.yaml @@ -27,6 +27,12 @@ jobs: with: fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: controller/go.mod + cache-dependency-path: controller/go.sum + - name: Run operator e2e test working-directory: controller - run: make test-operator-e2e -j3 --output-sync=target + run: make test-operator-e2e-ci -j3 --output-sync=target diff --git a/controller/Containerfile.prebuilt b/controller/Containerfile.prebuilt new file mode 100644 index 000000000..1783eea09 --- /dev/null +++ b/controller/Containerfile.prebuilt @@ -0,0 +1,13 @@ +# CI-only runtime image for host-compiled Go binaries. +# +# BIN selects which binary becomes the default entrypoint. +# COPY ${BIN} /entrypoint works because COPY supports ARG expansion, +# while ENTRYPOINT does not (and ubi-micro has no shell for shell-form). +FROM registry.access.redhat.com/ubi9/ubi-micro:9.8-1784702951@sha256:b1e86b97028b8fcfb6d85f997c39e6b6b67496163ef8d80d243220a4918e8bef +ARG BIN +WORKDIR / +COPY . . +# ENTRYPOINT can't expand ARG; fixed path lets one file serve all three images +COPY ${BIN} /entrypoint +USER 65532:65532 +ENTRYPOINT ["/entrypoint"] diff --git a/controller/Makefile b/controller/Makefile index 1fbd0ef9a..26b5c9400 100644 --- a/controller/Makefile +++ b/controller/Makefile @@ -27,6 +27,8 @@ else GOBIN=$(shell go env GOBIN) endif +GOARCH ?= $(shell go env GOARCH) + # CONTAINER_TOOL defines the container tool to be used for building images. # Be aware that the target commands are only tested with Docker which is # scaffolded by default. However, you might want to replace it to use other @@ -108,7 +110,13 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes ##@ Build .PHONY: build-operator build-operator: - make -C deploy/operator build-installer docker-build + $(MAKE) -C deploy/operator build-installer + $(MAKE) -C deploy/operator docker-build + +.PHONY: build-operator-ci +build-operator-ci: + $(MAKE) -C deploy/operator build-installer + $(MAKE) -C deploy/operator docker-build-ci .PHONY: build build: manifests generate fmt vet ## Build manager binary. @@ -135,6 +143,15 @@ docker-build: ## Build docker image with the manager. --build-arg BUILD_DATE=$(BUILD_DATE) \ -t ${IMG} -f Containerfile . +.PHONY: docker-build-ci +docker-build-ci: ## Build docker images from pre-compiled host binaries (fast CI path). + rm -rf bin/ci-stage && mkdir -p bin/ci-stage/controller bin/ci-stage/esc + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/manager cmd/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/router cmd/router/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/esc/exporter-set-controller cmd/exporter-set-controller/main.go + $(CONTAINER_TOOL) build --build-arg BIN=manager -t $(IMG) -f Containerfile.prebuilt bin/ci-stage/controller + $(CONTAINER_TOOL) build --build-arg BIN=exporter-set-controller -t $(EXPORTER_SET_CONTROLLER_IMG) -f Containerfile.prebuilt bin/ci-stage/esc + .PHONY: docker-build-exporter-set-controller docker-build-exporter-set-controller: ## Build docker image for the exporter-set-controller. $(CONTAINER_TOOL) build \ @@ -211,9 +228,17 @@ endif deploy-operator: docker-build docker-build-exporter-set-controller build-operator cluster grpcurl ## Deploy only the operator (without Jumpstarter CR) NETWORKING_MODE=ingress DEPLOY_JUMPSTARTER=false ./hack/deploy_with_operator.sh +.PHONY: deploy-operator-ci +deploy-operator-ci: docker-build-ci build-operator-ci cluster grpcurl ## CI-optimized: host-compiled binaries, no multi-stage container builds. + NETWORKING_MODE=ingress DEPLOY_JUMPSTARTER=false ./hack/deploy_with_operator.sh + .PHONY: test-operator-e2e test-operator-e2e: grpcurl deploy-operator make -C deploy/operator test-e2e + +.PHONY: test-operator-e2e-ci +test-operator-e2e-ci: grpcurl deploy-operator-ci ## CI-optimized e2e test (host-compiled Go, cached modules). + make -C deploy/operator test-e2e .PHONY: operator-logs operator-logs: kubectl logs -n jumpstarter-operator-system -l app.kubernetes.io/name=jumpstarter-operator -f diff --git a/controller/deploy/operator/Makefile b/controller/deploy/operator/Makefile index 89fef9e58..1ecdfc429 100644 --- a/controller/deploy/operator/Makefile +++ b/controller/deploy/operator/Makefile @@ -71,6 +71,8 @@ else GOBIN=$(shell go env GOBIN) endif +GOARCH ?= $(shell go env GOARCH) + # CONTAINER_TOOL defines the container tool to be used for building images. # Be aware that the target commands are only tested with Docker which is # scaffolded by default. However, you might want to replace it to use other @@ -177,6 +179,12 @@ docker-build: ## Build docker image with the manager. --build-arg BUILD_DATE=$(BUILD_DATE) \ -t ${IMG} ../../ -f ../../Containerfile.operator +.PHONY: docker-build-ci +docker-build-ci: ## CI-optimized: host-compiled binary, no multi-stage build. + rm -rf bin/ci-stage && mkdir -p bin/ci-stage + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/manager cmd/main.go + $(CONTAINER_TOOL) build --build-arg BIN=manager -t $(IMG) -f ../../Containerfile.prebuilt bin/ci-stage + .PHONY: docker-push docker-push: ## Push docker image with the manager. $(CONTAINER_TOOL) push ${IMG} From 64cd0e79dfa2b2827ecae4984b8de02a8cf936be Mon Sep 17 00:00:00 2001 From: Benny Zlotnik Date: Mon, 10 Aug 2026 11:38:15 +0300 Subject: [PATCH 3/3] perf(ci): apply host-compiled Go to deploy-kind job Use setup-go with cached modules and the deploy-ci target for the deploy-kind job too (was only on e2e-test-operator). Run with -j to parallelize cluster creation with Go compilation. Signed-off-by: Benny Zlotnik --- .github/workflows/controller-kind.yaml | 19 ++++++++++++++++--- controller/Makefile | 9 +++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/controller-kind.yaml b/.github/workflows/controller-kind.yaml index 91ec1a2ea..945d39146 100644 --- a/.github/workflows/controller-kind.yaml +++ b/.github/workflows/controller-kind.yaml @@ -6,6 +6,9 @@ on: paths: - 'controller/**' +permissions: + contents: read + jobs: deploy-kind: runs-on: ubuntu-latest @@ -15,9 +18,17 @@ jobs: with: fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: controller/go.mod + cache-dependency-path: | + controller/go.sum + controller/deploy/operator/go.sum + - name: Run make deploy working-directory: controller - run: make deploy + run: make deploy-ci -j4 --output-sync=target e2e-test-operator: runs-on: ubuntu-latest @@ -31,8 +42,10 @@ jobs: uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 with: go-version-file: controller/go.mod - cache-dependency-path: controller/go.sum + cache-dependency-path: | + controller/go.sum + controller/deploy/operator/go.sum - name: Run operator e2e test working-directory: controller - run: make test-operator-e2e-ci -j3 --output-sync=target + run: make test-operator-e2e-ci -j4 --output-sync=target diff --git a/controller/Makefile b/controller/Makefile index 26b5c9400..6ca9efa98 100644 --- a/controller/Makefile +++ b/controller/Makefile @@ -223,6 +223,10 @@ ifeq ($(SKIP_BUILD),) endif ./hack/deploy_with_operator.sh +.PHONY: deploy-ci +deploy-ci: cluster grpcurl $(if $(SKIP_BUILD),,docker-build-ci build-operator-ci) ## CI-optimized deploy: host-compiled binaries, no multi-stage container builds. + ./hack/deploy_with_operator.sh + .PHONY: deploy-operator deploy-operator: docker-build docker-build-exporter-set-controller build-operator cluster grpcurl ## Deploy only the operator (without Jumpstarter CR) @@ -234,11 +238,12 @@ deploy-operator-ci: docker-build-ci build-operator-ci cluster grpcurl ## CI-opti .PHONY: test-operator-e2e test-operator-e2e: grpcurl deploy-operator - make -C deploy/operator test-e2e + $(MAKE) -C deploy/operator test-e2e .PHONY: test-operator-e2e-ci test-operator-e2e-ci: grpcurl deploy-operator-ci ## CI-optimized e2e test (host-compiled Go, cached modules). - make -C deploy/operator test-e2e + $(MAKE) -C deploy/operator test-e2e + .PHONY: operator-logs operator-logs: kubectl logs -n jumpstarter-operator-system -l app.kubernetes.io/name=jumpstarter-operator -f