Skip to content

Commit face830

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 face830

File tree

7 files changed

+93
-12
lines changed

7 files changed

+93
-12
lines changed

build/Dockerfile

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
1+
# Define the default build as "prod", so that in the case that no build
2+
# arguments are provided, we build the optimized image.
3+
ARG BUILD_TYPE=prod
24

3-
LABEL maintainer "KubeSaw <devsandbox@redhat.com>"
4-
LABEL author "KubeSaw <devsandbox@redhat.com>"
5+
# Default production build setps for the operator.
6+
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS prod-build
7+
8+
LABEL maintainer="KubeSaw <devsandbox@redhat.com>"
9+
LABEL author="KubeSaw <devsandbox@redhat.com>"
510

611
ENV OPERATOR=/usr/local/bin/host-operator \
712
USER_UID=1001 \
@@ -17,3 +22,42 @@ RUN /usr/local/bin/user_setup
1722
ENTRYPOINT ["/usr/local/bin/entrypoint"]
1823

1924
USER ${USER_UID}
25+
26+
# A throwaway intermediate path which compiles Delve with the same Golang
27+
# version as the host computer. That ensures no discrepancies in the code
28+
# when setting up breakpoints when debgugging.
29+
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS debug-initial-build
30+
31+
# In this build path, the Golang version must be provided as a build argument,
32+
# which will ensure that Delve gets built with the same version the operator's
33+
# binary gets built with, to avoid any discrepancies.
34+
ARG GOLANG_VERSION
35+
36+
# Install the tools to be able to download and extract Golang.
37+
RUN microdnf install --assumeyes \
38+
curl \
39+
gzip \
40+
tar \
41+
&& microdnf clean all
42+
43+
# Download and extract Golang.
44+
RUN curl --location --output "/tmp/${GOLANG_VERSION}.linux-amd64.tar.gz" "https://go.dev/dl/${GOLANG_VERSION}.linux-amd64.tar.gz" \
45+
&& tar --directory /usr/local --extract --file "/tmp/${GOLANG_VERSION}.linux-amd64.tar.gz" \
46+
&& rm --force "/tmp/${GOLANG_VERSION}.linux-amd64.tar.gz"
47+
48+
# Put Golang in the path so that we can compile Delve with it.
49+
ENV PATH=$PATH:/usr/local/go/bin
50+
51+
# Build Delve and leave it in a temporary directory.
52+
RUN GOBIN=/tmp/bin go install github.com/go-delve/delve/cmd/dlv@latest
53+
54+
# In the case that the "BUILD_TYPE=debug" build argument is provided, we
55+
# "attempt to copy" the Delve executable into the production image. Since that
56+
# creates a dependency with the "debug-initial-build", that path is executed
57+
# by building the Delve executable.
58+
FROM prod-build AS debug-build
59+
COPY --from=debug-initial-build /tmp/bin/dlv /usr/local/bin/dlv
60+
61+
# When the no build argument is provided, we simply fall back to the
62+
# production build which comes with the optimized and minimized image.
63+
FROM ${BUILD_TYPE}-build AS final-build

build/bin/entrypoint

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

12-
exec ${OPERATOR} $@
12+
exec ${OPERATOR} "$@"

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 BUILD_TYPE='debug' --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)