Skip to content

Commit 1fba11a

Browse files
authored
Fix macOS and arm64-related issues (#1500)
Signed-off-by: Oleksii Kurinnyi <[email protected]>
1 parent 48f2d46 commit 1fba11a

File tree

10 files changed

+423
-38
lines changed

10 files changed

+423
-38
lines changed

CONTRIBUTING.md

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,71 @@ system.
6060

6161
#### macOS Specific Issues
6262

63-
On macOS, the default `make` utility might be outdated, leading to issues with some `Makefile` targets. To resolve this, it's recommended to install a newer version of `make` using Homebrew and ensure it's prioritized in your system's `$PATH`.
63+
On macOS, the default `make` utility might be outdated, leading to issues with some `Makefile` targets. To resolve this, it's recommended to install a newer version of `make` using Homebrew and ensure it's prioritized in your system's `$PATH`. Some scripts also require GNU `coreutils`.
6464

6565
> Note: `make` version `4.4.1` has been tested and confirmed to resolve these issues.
6666
67-
1. Install Homebrew `make`:
67+
1. Install Homebrew `make` and `coreutils`:
6868

6969
```bash
70-
brew install make
70+
brew install coreutils make
7171
```
7272

73-
2. Add the Homebrew `make` executable to your `$PATH` by adding the following line to your shell configuration file (e.g., `~/.zshrc`, `~/.bash_profile`):
73+
2. Add the Homebrew `make` and `coreutils` executables to your `$PATH`. Open a new terminal session and check if they are in your path:
74+
75+
```bash
76+
which make split
77+
```
78+
79+
If the output shows paths inside `/opt/homebrew/`, you are all set. Otherwise, add the following lines to your shell configuration file (e.g., `~/.zshrc`, `~/.bash_profile`):
7480

7581
```bash
7682
export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH"
83+
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
7784
```
7885

79-
After adding, reload your shell configuration (e.g., `source ~/.zshrc` or `source ~/.bash_profile`) or open a new terminal session.
86+
After adding, reload your shell configuration (e.g., `source ~/.zshrc` or `source ~/.bash_profile`) or open a new terminal session.
87+
88+
#### Multi-Architecture Builds
89+
90+
The DevWorkspace Operator supports building images for both amd64 and arm64 architectures. The build system automatically creates multi-architecture manifest lists that work on both platform types.
91+
92+
**Standard multi-arch workflow:**
93+
94+
```bash
95+
make docker-build # Builds both amd64 and arm64 images and creates a manifest list
96+
make docker-push # Pushes both images and the manifest list to the registry
97+
```
98+
99+
**Single-architecture builds (for faster development):**
100+
101+
```bash
102+
make docker-build-amd64 # Build only amd64
103+
make docker-push-amd64 # Push only amd64
104+
105+
make docker-build-arm64 # Build only arm64
106+
make docker-push-arm64 # Push only arm64
107+
```
108+
109+
#### Container Tool Selection
110+
111+
The build system automatically detects and uses either Docker or Podman, whichever is available. You can also explicitly choose the container tool:
112+
113+
**Using Docker:**
114+
115+
```bash
116+
export DOCKER=docker
117+
make docker-build
118+
```
119+
120+
**Using Podman:**
121+
122+
```bash
123+
export DOCKER=podman
124+
make docker-build
125+
```
126+
127+
**Note**: Docker with buildx extension provides the fastest multi-arch builds, while Podman builds architectures sequentially and combines them into manifest lists.
80128

81129
### Makefile
82130

Makefile

Lines changed: 149 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,64 @@ export DEFAULT_ROUTING ?= basic
3131
export KUBECONFIG ?= ${HOME}/.kube/config
3232
export DEVWORKSPACE_API_VERSION ?= a6ec0a38307b63a29fad2eea945cc69bee97a683
3333

34-
# Enable using Podman instead of Docker
35-
export DOCKER ?= docker
34+
# Container tool detection: auto-detect running Docker or Podman, allow override
35+
# Skip detection if we're inside a container build (when container tools aren't needed)
36+
ifeq (,$(DOCKER))
37+
# Check for running services first (prefer Docker for performance)
38+
ifneq (,$(shell docker info >/dev/null 2>&1 && echo "running"))
39+
export DOCKER := docker
40+
export CONTAINER_TOOL := docker
41+
else ifneq (,$(shell podman info >/dev/null 2>&1 && echo "running"))
42+
export DOCKER := podman
43+
export CONTAINER_TOOL := podman
44+
else
45+
# Fallback: check if binaries are installed but not running
46+
ifneq (,$(shell which docker 2>/dev/null))
47+
ifneq (,$(shell which podman 2>/dev/null))
48+
# Both installed but neither running
49+
ifeq ($(filter docker% _docker% build_bundle_and_index,$(MAKECMDGOALS)),)
50+
export DOCKER := not-available
51+
export CONTAINER_TOOL := not-available
52+
else
53+
$(error Both Docker and Podman are installed but neither is running. Please start Docker Desktop or Podman machine)
54+
endif
55+
else
56+
# Only Docker installed but not running
57+
ifeq ($(filter docker% _docker% build_bundle_and_index,$(MAKECMDGOALS)),)
58+
export DOCKER := not-available
59+
export CONTAINER_TOOL := not-available
60+
else
61+
$(error Docker is installed but not running. Please start Docker Desktop)
62+
endif
63+
endif
64+
else ifneq (,$(shell which podman 2>/dev/null))
65+
# Only Podman installed but not running
66+
ifeq ($(filter docker% _docker% build_bundle_and_index,$(MAKECMDGOALS)),)
67+
export DOCKER := not-available
68+
export CONTAINER_TOOL := not-available
69+
else
70+
$(error Podman is installed but not running. Please start Podman machine)
71+
endif
72+
else
73+
# Neither installed
74+
ifeq ($(filter docker% _docker% build_bundle_and_index,$(MAKECMDGOALS)),)
75+
export DOCKER := not-available
76+
export CONTAINER_TOOL := not-available
77+
else
78+
$(error Neither Docker nor Podman found. Please install Docker Desktop or Podman)
79+
endif
80+
endif
81+
endif
82+
else
83+
export CONTAINER_TOOL := $(DOCKER)
84+
endif
85+
86+
# Check if buildx is available for Docker (only if docker is available)
87+
ifeq ($(CONTAINER_TOOL),docker)
88+
BUILDX_AVAILABLE := $(shell docker buildx version >/dev/null 2>&1 && echo true || echo false)
89+
else
90+
BUILDX_AVAILABLE := false
91+
endif
3692

3793
#internal params
3894
DEVWORKSPACE_CTRL_SA=devworkspace-controller-serviceaccount
@@ -52,6 +108,10 @@ OPM_VERSION = v1.19.5
52108

53109
CRD_OPTIONS ?= "crd:crdVersions=v1"
54110

111+
# Default to linux for container builds, but allow override
112+
GOOS ?= linux
113+
# GOARCH is set dynamically in build/make/version.mk
114+
55115
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
56116
ifeq (,$(shell go env GOBIN))
57117
GOBIN=$(shell go env GOPATH)/bin
@@ -72,6 +132,9 @@ _print_vars:
72132
@echo " ROUTING_SUFFIX=$(ROUTING_SUFFIX)"
73133
@echo " DEFAULT_ROUTING=$(DEFAULT_ROUTING)"
74134
@echo " DEVWORKSPACE_API_VERSION=$(DEVWORKSPACE_API_VERSION)"
135+
@echo "Container tool:"
136+
@echo " CONTAINER_TOOL=$(CONTAINER_TOOL)"
137+
@echo " BUILDX_AVAILABLE=$(BUILDX_AVAILABLE)"
75138
@echo "Build environment:"
76139
@echo " Build Time: $(BUILD_TIME)"
77140
@echo " Go Package Path: $(GO_PACKAGE_PATH)"
@@ -185,23 +248,100 @@ generate: controller-gen
185248
### docker: Builds and pushes controller image
186249
docker: _print_vars docker-build docker-push
187250

188-
### docker-build: Builds the controller image
251+
### docker-build: Builds the multi-arch image (supports both amd64 and arm64)
189252
docker-build:
190-
$(DOCKER) build . -t ${DWO_IMG} -f build/Dockerfile
253+
@echo "Building multi-arch image ${DWO_IMG} for linux/amd64,linux/arm64 using $(CONTAINER_TOOL)"
254+
ifeq ($(CONTAINER_TOOL),docker)
255+
ifeq ($(BUILDX_AVAILABLE),false)
256+
$(error Docker buildx is required for multi-arch builds. Please update Docker or enable buildx)
257+
endif
258+
@echo "Using Docker buildx to build multi-arch images"
259+
$(MAKE) _docker-build-amd64 _docker-build-arm64
260+
@echo "✅ Built multi-arch images locally:"
261+
@echo " ${DWO_IMG}-amd64"
262+
@echo " ${DWO_IMG}-arm64"
263+
@echo "Note: Manifest list will be created during push to registry"
264+
else
265+
@echo "Using Podman to build multi-arch image"
266+
$(MAKE) _docker-build-amd64 _docker-build-arm64
267+
@echo "Creating manifest list for ${DWO_IMG} using Podman"
268+
@echo "Cleaning up any existing images/manifests with the same name"
269+
@$(DOCKER) manifest rm ${DWO_IMG} 2>/dev/null || echo " (manifest not found, continuing)"
270+
@$(DOCKER) rmi ${DWO_IMG} 2>/dev/null || echo " (image not found, continuing)"
271+
$(DOCKER) manifest create ${DWO_IMG} ${DWO_IMG}-amd64 ${DWO_IMG}-arm64
272+
endif
273+
274+
### _docker-build-amd64: Builds the amd64 image
275+
_docker-build-amd64:
276+
ifeq ($(CONTAINER_TOOL),docker)
277+
ifeq ($(BUILDX_AVAILABLE),false)
278+
$(error Docker buildx is required for platform-specific builds. Please update Docker or enable buildx)
279+
endif
280+
$(DOCKER) buildx build . --platform linux/amd64 --load -t ${DWO_IMG}-amd64 -f build/Dockerfile
281+
else
282+
$(DOCKER) build . --platform linux/amd64 -t ${DWO_IMG}-amd64 -f build/Dockerfile
283+
endif
284+
285+
### _docker-build-arm64: Builds the arm64 image
286+
_docker-build-arm64:
287+
ifeq ($(CONTAINER_TOOL),docker)
288+
ifeq ($(BUILDX_AVAILABLE),false)
289+
$(error Docker buildx is required for platform-specific builds. Please update Docker or enable buildx)
290+
endif
291+
$(DOCKER) buildx build . --platform linux/arm64 --load -t ${DWO_IMG}-arm64 -f build/Dockerfile
292+
else
293+
$(DOCKER) build . --platform linux/arm64 -t ${DWO_IMG}-arm64 -f build/Dockerfile
294+
endif
295+
296+
### docker-build-amd64: Builds only the amd64 image (for single-arch builds)
297+
docker-build-amd64: _docker-build-amd64
298+
299+
### docker-build-arm64: Builds only the arm64 image (for single-arch builds)
300+
docker-build-arm64: _docker-build-arm64
301+
302+
### docker-push-amd64: Pushes only the amd64 image (for single-arch workflows)
303+
docker-push-amd64: _docker-check-push
304+
$(DOCKER) push ${DWO_IMG}-amd64
305+
306+
### docker-push-arm64: Pushes only the arm64 image (for single-arch workflows)
307+
docker-push-arm64: _docker-check-push
308+
$(DOCKER) push ${DWO_IMG}-arm64
309+
310+
311+
### docker-push: Pushes the multi-arch image to the registry
312+
docker-push: _docker-check-push
313+
@echo "Pushing multi-arch image ${DWO_IMG} using $(CONTAINER_TOOL)"
314+
ifeq ($(CONTAINER_TOOL),docker)
315+
ifeq ($(BUILDX_AVAILABLE),false)
316+
$(error Docker buildx is required for multi-arch pushes. Please update Docker or enable buildx)
317+
endif
318+
@echo "Using Docker buildx to push multi-arch image"
319+
$(DOCKER) push ${DWO_IMG}-amd64
320+
$(DOCKER) push ${DWO_IMG}-arm64
321+
@echo "Creating and pushing manifest list using Docker buildx"
322+
$(DOCKER) buildx imagetools create -t ${DWO_IMG} ${DWO_IMG}-amd64 ${DWO_IMG}-arm64
323+
else
324+
@echo "Using Podman to push multi-arch image"
325+
$(DOCKER) push ${DWO_IMG}-amd64
326+
$(DOCKER) push ${DWO_IMG}-arm64
327+
@echo "Cleaning up any existing manifests before recreating"
328+
@$(DOCKER) manifest rm ${DWO_IMG} 2>/dev/null || echo " (manifest not found, continuing)"
329+
$(DOCKER) manifest create ${DWO_IMG} ${DWO_IMG}-amd64 ${DWO_IMG}-arm64
330+
$(DOCKER) manifest push ${DWO_IMG}
331+
endif
191332

192-
### docker-push: Pushes the controller image
193-
docker-push:
333+
### _docker-check-push: Asks for confirmation before pushing the image, unless running in CI
334+
_docker-check-push:
194335
ifneq ($(INITIATOR),CI)
195336
ifeq ($(DWO_IMG),quay.io/devfile/devworkspace-controller:next)
196337
@echo -n "Are you sure you want to push $(DWO_IMG)? [y/N] " && read ans && [ $${ans:-N} = y ]
197338
endif
198339
endif
199-
$(DOCKER) push ${DWO_IMG}
200340

201341
### compile-devworkspace-controller: Compiles the devworkspace-controller binary
202342
.PHONY: compile-devworkspace-controller
203343
compile-devworkspace-controller:
204-
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) GO111MODULE=on go build \
344+
CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) GO111MODULE=on go build \
205345
-a -o _output/bin/devworkspace-controller \
206346
-gcflags all=-trimpath=/ \
207347
-asmflags all=-trimpath=/ \
@@ -212,7 +352,7 @@ compile-devworkspace-controller:
212352
### compile-webhook-server: Compiles the webhook-server
213353
.PHONY: compile-webhook-server
214354
compile-webhook-server:
215-
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) GO111MODULE=on go build \
355+
CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) GO111MODULE=on go build \
216356
-o _output/bin/webhook-server \
217357
-gcflags all=-trimpath=/ \
218358
-asmflags all=-trimpath=/ \

build/Dockerfile

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414
#
1515

1616
# https://access.redhat.com/containers/?tab=tags#/registry.access.redhat.com/ubi9/go-toolset
17-
FROM registry.access.redhat.com/ubi9/go-toolset:1.24.6-1758501173 as builder
17+
# Use BUILDPLATFORM to ensure the builder always runs natively on the host machine
18+
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi9/go-toolset:1.24.6-1758501173 AS builder
19+
20+
# Accept TARGETARCH and TARGETPLATFORM, which are automatically passed by the builder
21+
ARG TARGETARCH
22+
ARG TARGETPLATFORM
23+
1824
ENV GOPATH=/go/
1925
USER root
2026
WORKDIR /devworkspace-operator
@@ -29,12 +35,12 @@ RUN go env GOPROXY && \
2935
# Copy the go source
3036
COPY . .
3137

32-
# compile workspace controller binaries, then webhook binaries
33-
RUN make compile-devworkspace-controller
34-
RUN make compile-webhook-server
38+
# Pass the target ARCH directly to make for robust cross-compilation
39+
RUN make GOARCH=${TARGETARCH} compile-devworkspace-controller
40+
RUN make GOARCH=${TARGETARCH} compile-webhook-server
3541

3642
# https://access.redhat.com/containers/?tab=tags#/registry.access.redhat.com/ubi9-minimal
37-
FROM registry.access.redhat.com/ubi9-minimal:9.6-1749489516
43+
FROM --platform=$TARGETPLATFORM registry.access.redhat.com/ubi9-minimal:9.6-1749489516
3844
RUN microdnf -y update && \
3945
microdnf clean all && \
4046
rm -rf /var/cache/yum && \
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# The base image is expected to contain
2+
# /bin/opm (with a serve subcommand) and /bin/grpc_health_probe
3+
FROM quay.io/operator-framework/opm:latest
4+
5+
# Configure the entrypoint and command
6+
ENTRYPOINT ["/bin/opm"]
7+
CMD ["serve", "/configs"]
8+
9+
# Copy declarative config root into image at /configs
10+
ADD olm-catalog/next /configs
11+
12+
# Set DC-specific label for the location of the DC root directory
13+
# in the image
14+
LABEL operators.operatorframework.io.index.configs.v1=/configs

build/make/deploy.mk

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ _gen_configuration_env:
5858
cat $(CONTROLLER_ENV_FILE)
5959

6060
_store_tls_cert:
61-
mkdir -p /tmp/k8s-webhook-server/serving-certs/
61+
mkdir -p $(TMPDIR)/k8s-webhook-server/serving-certs/
6262
ifeq ($(PLATFORM),kubernetes)
63-
$(K8S_CLI) get secret devworkspace-operator-webhook-cert -n $(NAMESPACE) -o json | jq -r '.data["tls.crt"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.crt
64-
$(K8S_CLI) get secret devworkspace-operator-webhook-cert -n $(NAMESPACE) -o json | jq -r '.data["tls.key"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.key
63+
$(K8S_CLI) get secret devworkspace-operator-webhook-cert -n $(NAMESPACE) -o json | jq -r '.data["tls.crt"]' | base64 -d > $(TMPDIR)/k8s-webhook-server/serving-certs/tls.crt
64+
$(K8S_CLI) get secret devworkspace-operator-webhook-cert -n $(NAMESPACE) -o json | jq -r '.data["tls.key"]' | base64 -d > $(TMPDIR)/k8s-webhook-server/serving-certs/tls.key
6565
else
66-
$(K8S_CLI) get secret devworkspace-webhookserver-tls -n $(NAMESPACE) -o json | jq -r '.data["tls.crt"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.crt
67-
$(K8S_CLI) get secret devworkspace-webhookserver-tls -n $(NAMESPACE) -o json | jq -r '.data["tls.key"]' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.key
66+
$(K8S_CLI) get secret devworkspace-webhookserver-tls -n $(NAMESPACE) -o json | jq -r '.data["tls.crt"]' | base64 -d > $(TMPDIR)/k8s-webhook-server/serving-certs/tls.crt
67+
$(K8S_CLI) get secret devworkspace-webhookserver-tls -n $(NAMESPACE) -o json | jq -r '.data["tls.key"]' | base64 -d > $(TMPDIR)/k8s-webhook-server/serving-certs/tls.key
6868
endif
6969

7070
_check_controller_running:

build/make/olm.mk

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ build_bundle_and_index: _print_vars _check_skopeo_installed _check_opm_version
4646
--bundle-repo $${DWO_BUNDLE_IMG%%:*} \
4747
--bundle-tag $${DWO_BUNDLE_IMG##*:} \
4848
--index-image $(DWO_INDEX_IMG) \
49-
--container-tool $(DOCKER)
49+
--container-tool $(CONTAINER_TOOL) \
50+
--arch $(ARCH)
5051

5152
### register_catalogsource: create the catalogsource to make the operator be available on the marketplace
5253
register_catalogsource: _check_skopeo_installed
@@ -82,5 +83,5 @@ _check_opm_version:
8283
_check_skopeo_installed:
8384
if ! command -v skopeo &> /dev/null; then \
8485
echo "Skopeo is required for building and deploying bundle, but it is not installed." ;\
85-
exit 1
86+
exit 1; \
8687
fi

build/make/version.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ export ARCH := amd64
2323
else ifeq (aarch64,$(ARCH))
2424
export ARCH := arm64
2525
endif
26+
27+
# Set GOARCH to the host architecture *only if it's not already set*
28+
# This allows `make GOARCH=amd64 ...` to override the default.
29+
GOARCH ?= $(ARCH)
30+
export GOARCH

0 commit comments

Comments
 (0)