diff --git a/build/Dockerfile b/build/Dockerfile index d5ad5aadd..4e1eaf9a0 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/host-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/controllers/toolchainconfig/env.go b/controllers/toolchainconfig/env.go index 4ec38a93b..573aa98af 100644 --- a/controllers/toolchainconfig/env.go +++ b/controllers/toolchainconfig/env.go @@ -1,3 +1,6 @@ package toolchainconfig -const RegistrationServiceImageEnvKey = "REGISTRATION_SERVICE_IMAGE" +const ( + RegistrationServiceImageEnvKey = "REGISTRATION_SERVICE_IMAGE" + RegistrationServiceCommandEnvKey = "REGISTRATION_SERVICE_COMMAND" +) diff --git a/controllers/toolchainconfig/toolchainconfig_controller.go b/controllers/toolchainconfig/toolchainconfig_controller.go index 25fa2bfbc..f6a7ac984 100644 --- a/controllers/toolchainconfig/toolchainconfig_controller.go +++ b/controllers/toolchainconfig/toolchainconfig_controller.go @@ -151,6 +151,16 @@ func getVars(namespace string, cfg ToolchainConfig) templateVars { vars["IMAGE"] = image vars.addIfNotEmpty("NAMESPACE", namespace) vars.addIfNotEmpty("REPLICAS", fmt.Sprint(cfg.RegistrationService().Replicas())) + + // Allow overriding the registration service's command via an environment + // variable. + command := os.Getenv(RegistrationServiceCommandEnvKey) + if command != "" { + vars["REGISTRATION_SERVICE_COMMAND"] = command + } else { + vars["REGISTRATION_SERVICE_COMMAND"] = `["registration-service"]` + } + return vars } diff --git a/deploy/templates/registration-service/registration-service.yaml b/deploy/templates/registration-service/registration-service.yaml index d39198aa9..f58d60944 100644 --- a/deploy/templates/registration-service/registration-service.yaml +++ b/deploy/templates/registration-service/registration-service.yaml @@ -96,13 +96,12 @@ objects: image: ${IMAGE} ports: - containerPort: 8080 # registration service - - containerPort: 8081 # proxy + - containerPort: 8081 # proxy - containerPort: 8082 # proxy metrics name: metrics - containerPort: 8083 # registration service metrics name: regsvc-metrics - command: - - registration-service + command: ${{REGISTRATION_SERVICE_COMMAND}} imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 3 @@ -141,7 +140,7 @@ objects: requests: cpu: "50m" memory: "100M" - + # route for the registration service - kind: Route apiVersion: v1 @@ -202,7 +201,7 @@ objects: run: registration-service type: ClusterIP sessionAffinity: null - + # route for the proxy - kind: Route apiVersion: v1 @@ -224,7 +223,7 @@ objects: tls: termination: edge wildcardPolicy: None - + # service associated with the proxy route - kind: Service apiVersion: v1 @@ -244,7 +243,7 @@ objects: run: registration-service type: ClusterIP sessionAffinity: null - + # internal service for the proxy, used by Prometheus to scrape the metrics - kind: Service apiVersion: v1 @@ -271,3 +270,8 @@ parameters: value: quay.io/openshiftio/codeready-toolchain/registration-service:latest - name: REPLICAS value: '3' + - name: REGISTRATION_SERVICE_COMMAND + description: >- + The command the registration service's container is going to be launch with. It must be specified as a + YAML array; for example: ["registration-service", "argument", "argument2", ..., "argumentN"]. + value: '["registration-service"]' diff --git a/make/go.mk b/make/go.mk index e1150872d..1d83d39eb 100644 --- a/make/go.mk +++ b/make/go.mk @@ -20,6 +20,19 @@ $(OUT_DIR)/operator: -o $(OUT_DIR)/bin/host-operator \ ./cmd/main.go +.PHONY: build-debug +## Build the operator without the optimizations and the inlining, to enable +## remote debugging via Delve. +build-debug: generate + @echo "building host-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/host-operator \ + ./cmd/main.go + .PHONY: vendor vendor: $(Q)go mod vendor diff --git a/make/podman.mk b/make/podman.mk index 3c9b3e972..f9245ec46 100644 --- a/make/podman.mk +++ b/make/podman.mk @@ -4,17 +4,29 @@ IMAGE_TAG ?= ${GIT_COMMIT_ID_SHORT} IMAGE ?= ${TARGET_REGISTRY}/${QUAY_NAMESPACE}/${GO_PACKAGE_REPO_NAME}:${IMAGE_TAG} QUAY_USERNAME ?= ${QUAY_NAMESPACE} IMAGE_PLATFORM ?= linux/amd64 +TARGET_ARCH ?= $(subst /,-,${IMAGE_PLATFORM}) .PHONY: podman-image ## Build the binary image podman-image: build $(Q)podman build --platform ${IMAGE_PLATFORM} -f build/Dockerfile -t ${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}" . + .PHONY: podman-push ## Push the binary image to quay.io registry podman-push: check-namespace podman-image $(Q)podman push ${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} + .PHONY: check-namespace check-namespace: ifeq ($(QUAY_NAMESPACE),${GO_PACKAGE_ORG_NAME})