diff --git a/Makefile b/Makefile index f46d42b..40dfccc 100644 --- a/Makefile +++ b/Makefile @@ -117,43 +117,25 @@ goimports: localbin ## Download goimports locally if necessary. If wrong version echo $(FORMATTER_VERSION) > hack/goimports_version ) ##@ Build - -.PHONY: build -build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go +build: generate ## Builds binaries for all components specified in COMPONENTS and all platforms specified in PLATFORMS. + @PLATFORMS=$(PLATFORMS) COMPONENTS=control-plane-operator ./hack/common/build-binary.sh .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/main.go start -# If you wish to build the manager image targeting other platforms you can use the --platform flag. -# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. -# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -.PHONY: docker-build -docker-build: ## Build docker image with the manager. - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -a -o bin/manager-linux.$(GOARCH) cmd/main.go - $(CONTAINER_TOOL) build --platform $(CONTAINER_PLATFORM) -t ${IMG} . +PLATFORMS ?= linux/arm64,linux/amd64 +EFFECTIVE_VERSION ?=$(shell cat ./VERSION) + +.PHONY: image-build +image-build: ## Builds the docker images for all components specified in COMPONENTS and all platforms specified in PLATFORMS. Requires 'make build' to have run before. + @PLATFORMS=$(PLATFORMS) COMPONENTS=control-plane-operator EFFECTIVE_VERSION=$(EFFECTIVE_VERSION) ./hack/common/build-image.sh + .PHONY: docker-push docker-push: ## Push docker image with the manager. $(CONTAINER_TOOL) push ${IMG} -# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple -# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ -# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) -# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. -PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le -.PHONY: docker-buildx -docker-buildx: ## Build and push docker image for the manager for cross-platform support - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name project-v3-builder - $(CONTAINER_TOOL) buildx use project-v3-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm project-v3-builder - rm Dockerfile.cross ##@ Deployment diff --git a/hack/common/Dockerfile b/hack/common/Dockerfile new file mode 100644 index 0000000..c6783d8 --- /dev/null +++ b/hack/common/Dockerfile @@ -0,0 +1,12 @@ +# Use distroless as minimal base image to package the component binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +ARG TARGETOS +ARG TARGETARCH +ARG COMPONENT +WORKDIR / +COPY bin/$COMPONENT-$TARGETOS.$TARGETARCH / +USER 65532:65532 + +# docker doesn't substitue args in ENTRYPOINT, so we replace this during the build script +ENTRYPOINT ["/"] diff --git a/hack/common/build-binary.sh b/hack/common/build-binary.sh new file mode 100755 index 0000000..94d5729 --- /dev/null +++ b/hack/common/build-binary.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail +source "$(realpath "$(dirname $0)/environment.sh")" + +echo +echo "> Building binaries ..." +( + cd "$PROJECT_ROOT" + for comp in ${COMPONENTS//,/ }; do + for pf in ${PLATFORMS//,/ }; do + echo "> Building binary for component '$comp' ($pf) ..." | indent 1 + os=${pf%/*} + arch=${pf#*/} + CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -a -o bin/${comp}-${os}.${arch} cmd/main.go | indent 2 + done + done +) diff --git a/hack/common/build-image.sh b/hack/common/build-image.sh new file mode 100755 index 0000000..c1595c5 --- /dev/null +++ b/hack/common/build-image.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -euo pipefail +source "$(realpath "$(dirname $0)/environment.sh")" + +if [[ -z ${IMAGE_REGISTRY:-} ]]; then + IMAGE_REGISTRY=$("$COMMON_SCRIPT_DIR/get-registry.sh" -i) +fi + +VERSION=$("$COMMON_SCRIPT_DIR/get-version.sh") + +DOCKER_BUILDER_NAME="mcp-multiarch-builder" +if ! docker buildx ls | grep "$DOCKER_BUILDER_NAME" >/dev/null; then + docker buildx create --name "$DOCKER_BUILDER_NAME" +fi + +# remove temporary Dockerfile on exit +trap "rm -f \"${PROJECT_ROOT}/Dockerfile.tmp\"" EXIT + +echo +echo "> Building images ..." +for comp in ${COMPONENTS//,/ }; do + for pf in ${PLATFORMS//,/ }; do + os=${pf%/*} + arch=${pf#*/} + img="${IMAGE_REGISTRY}/${comp}:${VERSION}-${os}-${arch}" + echo "> Building image for component '$comp' ($pf): $img ..." | indent 1 + cat "${COMMON_SCRIPT_DIR}/Dockerfile" | sed "s//$comp/g" > "${PROJECT_ROOT}/Dockerfile.tmp" + docker buildx build --builder ${DOCKER_BUILDER_NAME} --load --build-arg COMPONENT=${comp} --platform ${pf} -t $img -f Dockerfile.tmp "${PROJECT_ROOT}" | indent 2 + done +done + +docker buildx rm "$DOCKER_BUILDER_NAME" \ No newline at end of file diff --git a/hack/common/environment.sh b/hack/common/environment.sh new file mode 100755 index 0000000..2d03251 --- /dev/null +++ b/hack/common/environment.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +export COMMON_SCRIPT_DIR="$(realpath "$(dirname ${BASH_SOURCE[0]})")" +source "$COMMON_SCRIPT_DIR/lib.sh" +export PROJECT_ROOT="${PROJECT_ROOT:-$(realpath "$COMMON_SCRIPT_DIR/../..")}" +export COMPONENT_DEFINITION_FILE="${COMPONENT_DEFINITION_FILE:-"$PROJECT_ROOT/components/components.yaml"}" + +export LOCALBIN="${LOCALBIN:-"$PROJECT_ROOT/bin"}" +export HELM="${HELM:-"$LOCALBIN/helm"}" +export JQ="${JQ:-"$LOCALBIN/jq"}" +export FORMATTER=${FORMATTER:-"$LOCALBIN/goimports"} +export OCM="${OCM:-"$LOCALBIN/ocm"}" +export YAML2JSON="${YAML2JSON:-"$LOCALBIN/yaml2json"}" + +if [[ -f "$COMMON_SCRIPT_DIR/../environment.sh" ]]; then + source "$COMMON_SCRIPT_DIR/../environment.sh" +fi diff --git a/hack/common/get-registry.sh b/hack/common/get-registry.sh new file mode 100755 index 0000000..fe299fd --- /dev/null +++ b/hack/common/get-registry.sh @@ -0,0 +1,40 @@ +#!/bin/bash -eu + +set -euo pipefail + +if [[ -z ${BASE_REGISTRY:-} ]]; then + BASE_REGISTRY=ghcr.io/openmcp-project +fi + +if [[ -z ${IMAGE_REGISTRY:-} ]]; then + IMAGE_REGISTRY=$BASE_REGISTRY +fi +if [[ -z ${CHART_REGISTRY:-} ]]; then + CHART_REGISTRY=$BASE_REGISTRY/charts +fi +if [[ -z ${COMPONENT_REGISTRY:-} ]]; then + COMPONENT_REGISTRY=$BASE_REGISTRY/components +fi + +mode="BASE_" + +while [[ "$#" -gt 0 ]]; do + case ${1:-} in + "-i"|"--image") + mode="IMAGE_" + ;; + "-h"|"--helm") + mode="CHART_" + ;; + "-c"|"--component") + mode="COMPONENT_" + ;; + *) + echo "invalid argument: $1" 1>&2 + exit 1 + ;; + esac + shift +done + +eval echo "\$${mode}REGISTRY" diff --git a/hack/common/get-version.sh b/hack/common/get-version.sh new file mode 100755 index 0000000..afc4cc9 --- /dev/null +++ b/hack/common/get-version.sh @@ -0,0 +1,22 @@ +#!/bin/bash -eu + +if [[ -n ${EFFECTIVE_VERSION:-} ]] ; then + # running in the pipeline use the provided EFFECTIVE_VERSION + echo "$EFFECTIVE_VERSION" + exit 0 +fi + +set -euo pipefail +source "$(realpath "$(dirname $0)/environment.sh")" + +VERSION="$(cat "${PROJECT_ROOT}/VERSION")" + +( + cd "$PROJECT_ROOT" + + if [[ "$VERSION" = *-dev ]] ; then + VERSION="$VERSION-$(git rev-parse HEAD)" + fi + + echo "$VERSION" +) diff --git a/hack/common/lib.sh b/hack/common/lib.sh new file mode 100755 index 0000000..fbbf560 --- /dev/null +++ b/hack/common/lib.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# pipe some text into 'indent X' to indent each line by X levels (one 'level' being two spaces) +function indent() { + local level=${1:-""} + if [[ -z "$level" ]]; then + level=1 + fi + local spaces=$(($level * 2)) + local iv=$(printf %${spaces}s) + sed "s/^/$iv/" +} \ No newline at end of file