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