Skip to content

Commit 9a5e69f

Browse files
committed
feature: debug builds for the host operator
In order to be able to debug the operator 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 purpose, a new Dockerfile is added which can build the image that way, and the corresponding Make targets make it easy to kick off the whole process with a single command. The Makefile targets also enable the "toolchain-e2e" repository to easily build the "debug" image. Also, when Developer Sandbox is deployed locally, usually the registration service gets managed by the "ToolChain config controller", which launches it with a specific command. In order to be able to launch the registration service with Delve, a few minor modifications are made so that that launch command can be overridden. SANDBOX-1561
1 parent 410b521 commit 9a5e69f

File tree

7 files changed

+106
-9
lines changed

7 files changed

+106
-9
lines changed

build/Dockerfile.debug

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Compile Delve in an intermediate step.
2+
FROM registry.access.redhat.com/ubi8/ubi:latest AS delve-builder
3+
4+
# The Golang version must be provided as a build arguments, which will ensure
5+
# that Delve gets built with the same version the service's binary gets built
6+
# with, to avoid any discrepancies.
7+
ARG GOLANG_VERSION
8+
9+
# Install Tar to be able to extract Golang.
10+
RUN yum install --assumeyes \
11+
tar \
12+
&& yum clean all
13+
14+
# Download and extract Golang.
15+
RUN curl --location --output "${GOLANG_VERSION}.linux-amd64.tar.gz" "https://go.dev/dl/${GOLANG_VERSION}.linux-amd64.tar.gz" \
16+
&& curl --location --output "${GOLANG_VERSION}.linux-amd64.tar.gz.sha256" "https://go.dev/dl/${GOLANG_VERSION}.linux-amd64.tar.gz.sha256" \
17+
&& echo "$(cat ${GOLANG_VERSION}.linux-amd64.tar.gz.sha256) ${GOLANG_VERSION}.linux-amd64.tar.gz" | sha256sum --check \
18+
&& tar --directory /usr/local --extract --file "${GOLANG_VERSION}.linux-amd64.tar.gz" \
19+
&& rm --force "${GOLANG_VERSION}.linux-amd64.tar.gz"
20+
21+
# Put Golang in the path so that we can Delve with it.
22+
ENV PATH=$PATH:/usr/local/go/bin
23+
24+
# Build Delve and leave it in a temporary directory.
25+
RUN GOBIN=/tmp/bin go install github.com/go-delve/delve/cmd/dlv@latest
26+
27+
# Build the operator as normal.
28+
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
29+
30+
LABEL maintainer = "KubeSaw <devsandbox@redhat.com>"
31+
LABEL author = "KubeSaw <devsandbox@redhat.com>"
32+
33+
# Ensure that the "DEBUG_MODE" is enabled so that the binary executes with
34+
# Delve.
35+
ENV DEBUG_MODE=true \
36+
OPERATOR=/usr/local/bin/host-operator \
37+
USER_UID=1001 \
38+
USER_NAME=host-operator \
39+
LANG=en_US.utf8
40+
41+
# Install operator binary in the image.
42+
COPY build/_output/bin/host-operator ${OPERATOR}
43+
44+
COPY build/bin /usr/local/bin
45+
RUN /usr/local/bin/user_setup
46+
47+
# Copy Delve to the image so that we can execute the operator with it.
48+
COPY --from=delve-builder /tmp/bin/dlv /usr/local/bin/dlv
49+
50+
ENTRYPOINT ["/usr/local/bin/entrypoint"]
51+
52+
# Expose the debugger's port.
53+
EXPOSE 50000
54+
55+
USER ${USER_UID}

build/bin/entrypoint

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,9 @@ if ! whoami &>/dev/null; then
99
fi
1010
fi
1111

12-
exec ${OPERATOR} $@
12+
if [ -n "${DEBUG_MODE}" ]
13+
then
14+
exec /usr/local/bin/dlv --listen=:50000 --headless --continue --api-version=2 --accept-multiclient exec "${OPERATOR}" "$@"
15+
else
16+
exec ${OPERATOR} "$@"
17+
fi

controllers/toolchainconfig/env.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
package toolchainconfig
22

3-
const RegistrationServiceImageEnvKey = "REGISTRATION_SERVICE_IMAGE"
3+
const (
4+
RegistrationServiceImageEnvKey = "REGISTRATION_SERVICE_IMAGE"
5+
RegistrationServiceCommandEnvKey = "REGISTRATION_SERVICE_COMMAND"
6+
)

controllers/toolchainconfig/toolchainconfig_controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ func getVars(namespace string, cfg ToolchainConfig) templateVars {
151151
vars["IMAGE"] = image
152152
vars.addIfNotEmpty("NAMESPACE", namespace)
153153
vars.addIfNotEmpty("REPLICAS", fmt.Sprint(cfg.RegistrationService().Replicas()))
154+
155+
// Allow overriding the registration service's command via an environment
156+
// variable.
157+
command := os.Getenv(RegistrationServiceCommandEnvKey)
158+
if command != "" {
159+
vars["COMMAND"] = command
160+
} else {
161+
vars["COMMAND"] = `["registration-service"]`
162+
}
163+
154164
return vars
155165
}
156166

deploy/templates/registration-service/registration-service.yaml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,12 @@ objects:
9696
image: ${IMAGE}
9797
ports:
9898
- containerPort: 8080 # registration service
99-
- containerPort: 8081 # proxy
99+
- containerPort: 8081 # proxy
100100
- containerPort: 8082 # proxy metrics
101101
name: metrics
102102
- containerPort: 8083 # registration service metrics
103103
name: regsvc-metrics
104-
command:
105-
- registration-service
104+
command: ${{COMMAND}}
106105
imagePullPolicy: IfNotPresent
107106
livenessProbe:
108107
failureThreshold: 3
@@ -141,7 +140,7 @@ objects:
141140
requests:
142141
cpu: "50m"
143142
memory: "100M"
144-
143+
145144
# route for the registration service
146145
- kind: Route
147146
apiVersion: v1
@@ -202,7 +201,7 @@ objects:
202201
run: registration-service
203202
type: ClusterIP
204203
sessionAffinity: null
205-
204+
206205
# route for the proxy
207206
- kind: Route
208207
apiVersion: v1
@@ -224,7 +223,7 @@ objects:
224223
tls:
225224
termination: edge
226225
wildcardPolicy: None
227-
226+
228227
# service associated with the proxy route
229228
- kind: Service
230229
apiVersion: v1
@@ -244,7 +243,7 @@ objects:
244243
run: registration-service
245244
type: ClusterIP
246245
sessionAffinity: null
247-
246+
248247
# internal service for the proxy, used by Prometheus to scrape the metrics
249248
- kind: Service
250249
apiVersion: v1
@@ -271,3 +270,5 @@ parameters:
271270
value: quay.io/openshiftio/codeready-toolchain/registration-service:latest
272271
- name: REPLICAS
273272
value: '3'
273+
- name: COMMAND
274+
value: '["registration-service"]'

make/go.mk

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ $(OUT_DIR)/operator:
2020
-o $(OUT_DIR)/bin/host-operator \
2121
./cmd/main.go
2222

23+
.PHONY: build-debug
24+
## Build the operator without the optimizations and the inlining, to enable
25+
## remote debugging via Delve.
26+
build-debug: generate
27+
@echo "building host-operator in ${GO_PACKAGE_PATH}"
28+
$(Q)go version
29+
$(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \
30+
go build ${V_FLAG} \
31+
-gcflags "all=-N -l" \
32+
-ldflags "-X ${GO_PACKAGE_PATH}/version.Commit=${GIT_COMMIT_ID} -X ${GO_PACKAGE_PATH}/version.BuildTime=${BUILD_TIME}" \
33+
-o $(OUT_DIR)/bin/host-operator \
34+
./cmd/main.go
35+
2336
.PHONY: vendor
2437
vendor:
2538
$(Q)go mod vendor

make/podman.mk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@ IMAGE_PLATFORM ?= linux/amd64
1010
podman-image: build
1111
$(Q)podman build --platform ${IMAGE_PLATFORM} -f build/Dockerfile -t ${IMAGE} .
1212

13+
## Build the operator's image with Delve on it so that it is ready to attach a
14+
## debugger to it.
15+
podman-image-debug: build-debug
16+
$(Q) podman build --platform ${IMAGE_PLATFORM} --build-arg GOLANG_VERSION="$$(go version | awk '{print $$3}')" --file build/Dockerfile.debug --tag ${IMAGE} .
17+
1318
.PHONY: podman-push
1419
## Push the binary image to quay.io registry
1520
podman-push: check-namespace podman-image
1621
$(Q)podman push ${IMAGE}
1722

23+
.PHONY: podman-push-debug
24+
## Push the image with the debugger in it to the repository.
25+
podman-push-debug: check-namespace podman-image-debug
26+
$(Q)podman push ${IMAGE}
27+
1828
.PHONY: check-namespace
1929
check-namespace:
2030
ifeq ($(QUAY_NAMESPACE),${GO_PACKAGE_ORG_NAME})

0 commit comments

Comments
 (0)