diff --git a/build/Dockerfile b/build/Dockerfile index 17797ce7c..bae2af301 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,7 +1,12 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal:latest +# Define the default build as "prod", so that in the case that no build +# arguments are provided, we build the optimized image. +ARG BUILD_TYPE=prod -LABEL maintainer "KubeSaw " -LABEL author "KubeSaw " +# Default production build steps for the operator. +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS prod-build + +LABEL maintainer="KubeSaw " +LABEL author="KubeSaw " ENV OPERATOR=/usr/local/bin/member-operator \ USER_UID=1001 \ @@ -16,4 +21,47 @@ RUN /usr/local/bin/user_setup ENTRYPOINT ["/usr/local/bin/entrypoint"] +# A throwaway intermediate path which compiles Delve with the same Golang +# version as the host computer. That ensures no discrepancies in the code +# when setting up breakpoints when debugging. +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS debug-initial-build + +# In this build path, the Golang version must be provided as a build argument, +# which will ensure that Delve gets built with the same version the operator's +# binary gets built with, to avoid any discrepancies. +ARG GOLANG_VERSION +RUN test -n "${GOLANG_VERSION}" || (echo 'The GOLANG_VERSION build parameter is required to build the debug image. Please provide it with "--build-arg GOLANG_VERSION=goX.Y.Z"' && exit 1) + +# Install the tools to be able to download and extract Golang. +RUN microdnf install --assumeyes \ + curl \ + gzip \ + tar \ + && microdnf clean all + +# The target architecture in which the binary will be built. +ARG TARGET_ARCH='linux-amd64' + +# Download and extract Golang. +RUN curl --location --output "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" "https://go.dev/dl/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" \ + && tar --directory /usr/local --extract --file "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" \ + && rm --force "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" + +# Put Golang in the path so that we can compile Delve with it. +ENV PATH=$PATH:/usr/local/go/bin + +# Build Delve and leave it in a temporary directory. +RUN GOBIN=/tmp/bin go install github.com/go-delve/delve/cmd/dlv@latest + +# In the case that the "BUILD_TYPE=debug" build argument is provided, we +# "attempt to copy" the Delve executable into the production image. Since that +# creates a dependency with the "debug-initial-build", that path is executed +# by building the Delve executable. +FROM prod-build AS debug-build +COPY --from=debug-initial-build /tmp/bin/dlv /usr/local/bin/dlv + +# When the no build argument is provided, we simply fall back to the +# production build which comes with the optimized and minimized image. +FROM ${BUILD_TYPE}-build AS final-build + USER ${USER_UID} diff --git a/build/Dockerfile.webhook b/build/Dockerfile.webhook index aa3757817..63b70e093 100644 --- a/build/Dockerfile.webhook +++ b/build/Dockerfile.webhook @@ -1,7 +1,12 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal:latest +# Define the default build as "prod", so that in the case that no build +# arguments are provided, we build the optimized image. +ARG BUILD_TYPE=prod -LABEL maintainer "KubeSaw " -LABEL author "KubeSaw " +# Default production build steps for the operator. +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS prod-build + +LABEL maintainer="KubeSaw " +LABEL author="KubeSaw " ENV WEBHOOK=/usr/local/bin/member-operator-webhook \ USER_UID=1001 \ @@ -16,4 +21,48 @@ RUN /usr/local/bin/user_setup ENTRYPOINT ["/usr/local/bin/entrypoint"] +# A throwaway intermediate path which compiles Delve with the same Golang +# version as the host computer. That ensures no discrepancies in the code +# when setting up breakpoints when debugging. +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS debug-initial-build + +# In this build path, the Golang version must be provided as a build argument, +# which will ensure that Delve gets built with the same version the operator's +# binary gets built with, to avoid any discrepancies. +ARG GOLANG_VERSION +RUN test -n "${GOLANG_VERSION}" || (echo 'The GOLANG_VERSION build parameter is required to build the debug image. Please provide it with "--build-arg GOLANG_VERSION=goX.Y.Z"' && exit 1) + +# Install the tools to be able to download and extract Golang. +RUN microdnf install --assumeyes \ + curl \ + gzip \ + tar \ + && microdnf clean all + +# The target architecture in which the binary will be built. +ARG TARGET_ARCH='linux-amd64' + +# Download and extract Golang. +RUN curl --location --output "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" "https://go.dev/dl/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" \ + && tar --directory /usr/local --extract --file "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" \ + && rm --force "/tmp/${GOLANG_VERSION}.${TARGET_ARCH}.tar.gz" + +# Put Golang in the path so that we can compile Delve with it. +ENV PATH=$PATH:/usr/local/go/bin + +# Build Delve and leave it in a temporary directory. +RUN GOBIN=/tmp/bin go install github.com/go-delve/delve/cmd/dlv@latest + +# In the case that the "BUILD_TYPE=debug" build argument is provided, we +# "attempt to copy" the Delve executable into the production image. Since that +# creates a dependency with the "debug-initial-build", that path is executed +# by building the Delve executable. +FROM prod-build AS debug-build + +COPY --from=debug-initial-build /tmp/bin/dlv /usr/local/bin/dlv + +# When the no build argument is provided, we simply fall back to the +# production build which comes with the optimized and minimized image. +FROM ${BUILD_TYPE}-build AS final-build + USER ${USER_UID} diff --git a/make/go.mk b/make/go.mk index c905ce417..05a402476 100644 --- a/make/go.mk +++ b/make/go.mk @@ -23,6 +23,25 @@ $(OUT_DIR)/operator: -o $(OUT_DIR)/bin/member-operator-webhook \ cmd/webhook/main.go +.PHONY: build-debug +## Build the operator's image with Delve on it so that it is ready to attach a +## debugger to it. +build-debug: + @echo "building member-operator in ${GO_PACKAGE_PATH}" + $(Q)go version + $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ + go build ${V_FLAG} \ + -gcflags "all=-N -l" \ + -ldflags "-X ${GO_PACKAGE_PATH}/version.Commit=${GIT_COMMIT_ID} -X ${GO_PACKAGE_PATH}/version.BuildTime=${BUILD_TIME}" \ + -o $(OUT_DIR)/bin/member-operator \ + ./cmd/main.go + $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ + go build ${V_FLAG} \ + -gcflags "all=-N -l" \ + -ldflags "-X ${GO_PACKAGE_PATH}/version.Commit=${GIT_COMMIT_ID} -X ${GO_PACKAGE_PATH}/version.BuildTime=${BUILD_TIME}" \ + -o $(OUT_DIR)/bin/member-operator-webhook \ + cmd/webhook/main.go + .PHONY: vendor vendor: $(Q)go mod vendor diff --git a/make/podman.mk b/make/podman.mk index e637e7bc2..f63cd0b14 100644 --- a/make/podman.mk +++ b/make/podman.mk @@ -5,6 +5,7 @@ IMAGE ?= ${TARGET_REGISTRY}/${QUAY_NAMESPACE}/${GO_PACKAGE_REPO_NAME}:${IMAGE_TA QUAY_USERNAME ?= ${QUAY_NAMESPACE} WEBHOOK_IMAGE ?= ${TARGET_REGISTRY}/${QUAY_NAMESPACE}/${GO_PACKAGE_REPO_NAME}-webhook:${IMAGE_TAG} IMAGE_PLATFORM ?= linux/amd64 +TARGET_ARCH ?= $(subst /,-,${IMAGE_PLATFORM}) .PHONY: podman-image ## Build the binary image @@ -12,12 +13,25 @@ podman-image: build $(Q)podman build --platform ${IMAGE_PLATFORM} -f build/Dockerfile -t ${IMAGE} . $(Q)podman build --platform ${IMAGE_PLATFORM} -f build/Dockerfile.webhook -t ${WEBHOOK_IMAGE} . +.PHONY: podman-image-debug +## Build the operator's image with Delve on it so that it is ready to attach a +## debugger to it. +podman-image-debug: build-debug + $(Q) podman build --platform "${IMAGE_PLATFORM}" --build-arg BUILD_TYPE='debug' --build-arg GOLANG_VERSION="$$(go version | awk '{print $$3}')" --build-arg TARGET_ARCH="${TARGET_ARCH}" --file build/Dockerfile --tag "${IMAGE}" . + $(Q) podman build --platform "${IMAGE_PLATFORM}" --build-arg BUILD_TYPE='debug' --build-arg GOLANG_VERSION="$$(go version | awk '{print $$3}')" --build-arg TARGET_ARCH="${TARGET_ARCH}" --file build/Dockerfile.webhook --tag "${WEBHOOK_IMAGE}" . + .PHONY: podman-push ## Push the binary image to quay.io registry podman-push: check-namespace podman-image $(Q)podman push ${IMAGE} $(Q)podman push ${WEBHOOK_IMAGE} +.PHONY: podman-push-debug +## Push the image with the debugger in it to the repository. +podman-push-debug: check-namespace podman-image-debug + $(Q)podman push ${IMAGE} + $(Q)podman push ${WEBHOOK_IMAGE} + .PHONY: check-namespace check-namespace: ifeq ($(QUAY_NAMESPACE),${GO_PACKAGE_ORG_NAME})