From edc063d6ebe408aabc7ba4019aa60ab72b4e1503 Mon Sep 17 00:00:00 2001 From: MikelAlejoBR Date: Tue, 23 Dec 2025 10:41:19 -0500 Subject: [PATCH] feature: enable debug builds for the service In order to be able to debug the service live in the local cluster, we need the binary to be executed with Delve instead, and we need it to be built without any code optimizations or minimization so that the breakpoints work. For that purose, a new Dockerfile has been added which can build the image that way, and the corresponding Make target make it easy to kick off the whole process with a single command. The Makefile target also enable the "toolchain-e2e" repository to easily build the "debug" image. The "scripts/deploy-dev.sh" script is also modified to be able to refresh the registration service with the debug image if so desired. SANDBOX-1561 --- build/Dockerfile.debug | 51 ++++++++++++++++++++++++++++++++++++++++++ make/go.mk | 15 +++++++------ make/podman.mk | 10 +++++++++ scripts/deploy-dev.sh | 45 +++++++++++++++++++++++++++++-------- 4 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 build/Dockerfile.debug diff --git a/build/Dockerfile.debug b/build/Dockerfile.debug new file mode 100644 index 00000000..23ecf70f --- /dev/null +++ b/build/Dockerfile.debug @@ -0,0 +1,51 @@ +# Compile Delve in an intermediate step. +FROM registry.access.redhat.com/ubi8/ubi:latest AS delve-builder + +# The Golang version must be provided as a build arguments, which will ensure +# that Delve gets built with the same version the service's binary gets built +# with, to avoid any discrepancies. +ARG GOLANG_VERSION + +# Install Tar to be able to extract Golang. +RUN yum install --assumeyes \ + tar \ + && yum clean all + +# Download and extract Golang. +RUN curl --location --output ${GOLANG_VERSION}.linux-amd64.tar.gz https://go.dev/dl/${GOLANG_VERSION}.linux-amd64.tar.gz \ + && tar --directory /usr/local --extract --file ${GOLANG_VERSION}.linux-amd64.tar.gz \ + && rm --force ${GOLANG_VERSION}.linux-amd64.tar.gz + +# Put Golang in the path so that we can 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 + +# Build the final image. +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest + +LABEL maintainer = "KubeSaw " +LABEL author = "KubeSaw " + +ENV REG_SERVICE=/usr/local/bin/registration-service \ + USER_UID=1001 \ + USER_NAME=registration-service \ + LANG=en_US.utf8 + +COPY build/bin /usr/local/bin +RUN /usr/local/bin/user_setup + +# Install registration-service binary +COPY build/_output/bin/registration-service ${REG_SERVICE} + +# Copy Delve to the image so that we can execute the operator with it. +COPY --from=delve-builder /tmp/bin/dlv /usr/local/bin/dlv + +# Execute the service with the "dlv" command to enable debugging. +ENTRYPOINT ["dlv", "--listen=:50000", "--headless", "--continue", "--api-version=2", "--accept-multiclient", "exec", "/usr/local/bin/registration-service"] + +# Expose the debugger's port along with the service's regular ports. +EXPOSE 50000 8080 8081 8082 + +USER ${USER_UID} diff --git a/make/go.mk b/make/go.mk index dc402849..db734d7e 100644 --- a/make/go.mk +++ b/make/go.mk @@ -11,12 +11,13 @@ goarch ?= $(shell go env GOARCH) # builds the production binary build: build-prod -# buils a development binary that has no bundled assets but reads them -# from the filesystem. Use only for development. -## builds development binary +# Builds a development binary that has no bundled assets but reads them from +# the filesystem. It also builds the binary without any optimizations or +# code inlining, so that Delve can be used to execute it. Use only for +# development. build-dev: $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ - go build ${V_FLAG} -ldflags="${LDFLAGS}" \ + go build -gcflags "all=-N -l" ${V_FLAG} -ldflags="${LDFLAGS}" \ -tags dev \ -o $(OUT_DIR)/bin/registration-service \ cmd/main.go @@ -38,13 +39,13 @@ vendor: verify-dependencies: tidy vet build test lint-go-code .PHONY: tidy -tidy: +tidy: go mod tidy .PHONY: vet vet: go vet ./... - + .PHONY: pre-verify pre-verify: - echo "No Pre-requisite needed" \ No newline at end of file + echo "No Pre-requisite needed" diff --git a/make/podman.mk b/make/podman.mk index 3c9b3e97..442550e5 100644 --- a/make/podman.mk +++ b/make/podman.mk @@ -10,11 +10,21 @@ IMAGE_PLATFORM ?= linux/amd64 podman-image: build $(Q)podman build --platform ${IMAGE_PLATFORM} -f build/Dockerfile -t ${IMAGE} . +## Build the operator's image with Delve on it so that it is ready to attach a +## debugger to it. +podman-image-debug: build-dev + $(Q)podman build --platform ${IMAGE_PLATFORM} --build-arg GOLANG_VERSION="$$(go version | awk '{print $$3}')" --file build/Dockerfile.debug --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}) diff --git a/scripts/deploy-dev.sh b/scripts/deploy-dev.sh index 77dd3797..1874da7f 100755 --- a/scripts/deploy-dev.sh +++ b/scripts/deploy-dev.sh @@ -32,16 +32,36 @@ setup() { } refresh() { + # Create a flag for when we need to build the "debug" images instead. + if [[ "$1" == "debug" ]] + then + DEBUG=true + else + DEBUG=false + fi + # build the registration service echo "📦 building the binary" - VERBOSE=0 make build + if [[ "${DEBUG}" = true ]] + then + VERBOSE=0 make build-dev + else + VERBOSE=0 make build + fi echo "✅ done" echo "📦 building the image" REGISTRY_ROUTE=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}') TIMESTAMP=$(date +%s) IMAGE_NAME=registration-service:dev-$TIMESTAMP - podman build -f build/Dockerfile -t $REGISTRY_ROUTE/$HOST_NS/$IMAGE_NAME . + + # Build the debug image with podman. + if [[ "${DEBUG}" = true ]] + then + IMAGE="${REGISTRY_ROUTE}/${HOST_NS}/${IMAGE_NAME}" VERBOSE=0 make podman-image-debug + else + IMAGE="${REGISTRY_ROUTE}/${HOST_NS}/${IMAGE_NAME}" VERBOSE=0 make podman-image + fi echo "✅ done" # copy/replace the binary into the pod's container @@ -51,17 +71,23 @@ refresh() { # restart the process in the pod's container INTERNAL_REGISTRY=image-registry.openshift-image-registry.svc:5000 - echo "✏️ patching the deployment with image $INTERNAL_REGISTRY/$HOST_NS/$IMAGE_NAME" - oc patch deployment/registration-service -n $HOST_NS --type='json' -p="[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/image\", \"value\":\"$INTERNAL_REGISTRY/$HOST_NS/$IMAGE_NAME\"}]" + echo "✏️ patching the deployment with the pushed image ${INTERNAL_REGISTRY}/${HOST_NS}/${IMAGE_NAME}" + oc patch deployment/registration-service -n ${HOST_NS} --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"'"${INTERNAL_REGISTRY}/${HOST_NS}/${IMAGE_NAME}"'"}]' + + if [[ "${DEBUG}" = true ]] + then + echo "✏️ patching the deployment's command to execute the registration service with Delve instead" + oc patch deployment/registration-service --namespace "${HOST_NS}" --type='json' --patch='[{"op": "replace", "path": "/spec/template/spec/containers/0/command", "value": ["dlv", "--listen=:50000", "--headless", "--continue", "--api-version=2", "--accept-multiclient", "exec", "/usr/local/bin/registration-service"]}' + fi # oc rollout restart deployment/registration-service # Et voilà! echo "👋 all done!" } -if [ -z "$HOST_NS" ]; then - echo "HOST_NS is not set"; - exit 1; +if [ -z "$HOST_NS" ]; then + echo "HOST_NS is not set"; + exit 1; fi if declare -f "$1" > /dev/null @@ -72,8 +98,9 @@ else # Show a helpful error echo "'$1' is not a valid command" >&2 echo "available commands:" - echo "setup Configure the deployment with a single pod" - echo "refresh Rebuild the registration-service and update the pod" + echo "setup Configure the deployment with a single pod" + echo "refresh Rebuild the registration-service and update the pod" + echo "refresh debug Rebuild the registration service with Delve on it listening on port 50000" echo "" exit 1 fi \ No newline at end of file