# Copyright Intel Corporation, 2022 IBM Corp.
# Copyright (c) 2025 NVIDIA Corporation
#
# SPDX-License-Identifier: Apache-2.0

#### Nydus snapshotter & nydus image

FROM golang:1.24-alpine AS nydus-binary-downloader

COPY versions.yaml /tmp/versions.yaml

RUN \
	set -e && \
	apk add --no-cache curl yq-go && \
	NYDUS_SNAPSHOTTER_VERSION="$(yq eval -e '.externals.nydus-snapshotter.version | explode(.)' /tmp/versions.yaml)" && \
	NYDUS_SNAPSHOTTER_REPO="$(yq eval -e '.externals.nydus-snapshotter.url | explode(.)' /tmp/versions.yaml)" && \
	mkdir -p /opt/nydus-snapshotter && \
	ARCH="$(uname -m)" && \
	if [ "${ARCH}" = "x86_64" ]; then ARCH=amd64 ; fi && \
	if [ "${ARCH}" = "aarch64" ]; then ARCH=arm64; fi && \
	curl -fOL --progress-bar "${NYDUS_SNAPSHOTTER_REPO}/releases/download/${NYDUS_SNAPSHOTTER_VERSION}/nydus-snapshotter-${NYDUS_SNAPSHOTTER_VERSION}-linux-${ARCH}.tar.gz" && \
	tar xvzpf "nydus-snapshotter-${NYDUS_SNAPSHOTTER_VERSION}-linux-${ARCH}.tar.gz" -C /opt/nydus-snapshotter && \
	rm "nydus-snapshotter-${NYDUS_SNAPSHOTTER_VERSION}-linux-${ARCH}.tar.gz"

#### Build binary package
FROM ubuntu:22.04 AS rust-builder

# Default to Rust 1.90.0
ARG RUST_TOOLCHAIN=1.90.0
ENV DEBIAN_FRONTEND=noninteractive
ENV RUSTUP_HOME="/opt/rustup"
ENV CARGO_HOME="/opt/cargo"
ENV PATH="/opt/cargo/bin/:${PATH}"

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN \
	mkdir ${RUSTUP_HOME} ${CARGO_HOME} && \
	chmod -R a+rwX ${RUSTUP_HOME} ${CARGO_HOME}

RUN \
	apt-get update && \
	apt-get --no-install-recommends -y install \
		ca-certificates \
		curl \
		gcc \
		libc6-dev \
		musl-tools && \
	apt-get clean && rm -rf /var/lib/apt/lists/ && \
	curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${RUST_TOOLCHAIN} && \
	rustup component add rustfmt clippy

# Build from the repository root so kata-deploy uses the root Cargo workspace:
#   docker build -f tools/packaging/kata-deploy/Dockerfile .
WORKDIR /kata

COPY Cargo.toml Cargo.lock ./
COPY src ./src
COPY tools/packaging/kata-deploy/binary ./tools/packaging/kata-deploy/binary

# Install target and run tests based on architecture
# - AMD64/arm64: use musl for fully static binaries
# - PPC64le/s390x: use glibc (musl has issues on these platforms)
RUN \
	HOST_ARCH="$(uname -m)"; \
	rust_arch=""; \
	rust_target=""; \
	case "${HOST_ARCH}" in \
		"x86_64") \
			rust_arch="x86_64"; \
			rust_target="${rust_arch}-unknown-linux-musl"; \
			echo "Installing musl target for ${rust_target}"; \
			rustup target add "${rust_target}"; \
			;; \
		"aarch64") \
			rust_arch="aarch64"; \
			rust_target="${rust_arch}-unknown-linux-musl"; \
			echo "Installing musl target for ${rust_target}"; \
			rustup target add "${rust_target}"; \
			;; \
		"ppc64le") \
			rust_arch="powerpc64le"; \
			rust_target="${rust_arch}-unknown-linux-gnu"; \
			echo "Using glibc target for ${rust_target} (musl is not well supported on ppc64le)"; \
			;; \
		"s390x") \
			rust_arch="s390x"; \
			rust_target="${rust_arch}-unknown-linux-gnu"; \
			echo "Using glibc target for ${rust_target} (musl is not well supported on s390x)"; \
			;; \
		*) echo "Unsupported architecture: ${HOST_ARCH}" && exit 1 ;; \
	esac; \
	echo "${rust_target}" > /tmp/rust_target

# Verify code formatting and run cargo check before tests and build
RUN \
	set -e && \
	rust_target="$(cat /tmp/rust_target)" && \
	echo "Checking code formatting..." && \
	cargo fmt -p kata-deploy --check && \
	echo "Code formatting check passed!" && \
	echo "Running cargo clippy with target ${rust_target}..." && \
	cargo clippy -p kata-deploy --all-targets --all-features --release --locked --target "${rust_target}" -- -D warnings && \
	echo "Cargo clippy passed!"

# Run tests using --test-threads=1 to prevent environment variable pollution between tests,
# and this is fine as we'll never ever have multiple binaries running at the same time.
RUN \
	rust_target="$(cat /tmp/rust_target)"; \
	echo "Running binary tests with target ${rust_target}..." && \
	RUSTFLAGS="-D warnings" cargo test -p kata-deploy --target "${rust_target}" -- --test-threads=1 && \
	echo "All tests passed!"

RUN \
	rust_target="$(cat /tmp/rust_target)"; \
	echo "Building kata-deploy binary for ${rust_target}..." && \
	RUSTFLAGS="-D warnings" cargo build --release -p kata-deploy --target "${rust_target}" && \
	mkdir -p /kata-deploy/bin && \
	cp "/kata/target/${rust_target}/release/kata-deploy" /kata-deploy/bin/kata-deploy && \
	echo "Cleaning up build artifacts to save disk space..." && \
	rm -rf /kata/target && \
	cargo clean

#### Extract kata artifacts
FROM alpine:3.22 AS artifact-extractor

ARG KATA_ARTIFACTS=tools/packaging/kata-deploy/kata-static.tar.zst
ARG DESTINATION=/opt/kata-artifacts

COPY ${KATA_ARTIFACTS} /tmp/
RUN \
	apk add --no-cache tar zstd util-linux-misc && \
	mkdir -p "${DESTINATION}" && \
	tar --zstd -xf "/tmp/$(basename "${KATA_ARTIFACTS}")" -C "${DESTINATION}" && \
	rm -f "/tmp/$(basename "${KATA_ARTIFACTS}")"

#### Prepare runtime dependencies (nsenter and required libraries)
# This stage assembles all runtime dependencies based on architecture
# using ldd to find exact library dependencies
FROM debian:trixie-slim AS runtime-assembler

ARG DESTINATION=/opt/kata-artifacts

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN \
	apt-get update && \
	apt-get --no-install-recommends -y install \
		util-linux && \
	apt-get clean && rm -rf /var/lib/apt/lists/

# Copy the built binary to analyze its dependencies
COPY --from=rust-builder /kata-deploy/bin/kata-deploy /tmp/kata-deploy

# Create output directories
RUN mkdir -p /output/lib /output/lib64 /output/usr/bin

# Use ldd to find and copy all required libraries for the kata-deploy binary and nsenter
RUN \
	HOST_ARCH="$(uname -m)"; \
	echo "Preparing runtime dependencies for ${HOST_ARCH}"; \
	case "${HOST_ARCH}" in \
		"ppc64le"|"s390x") \
			echo "Using glibc - copying libraries based on ldd output"; \
			\
			# Copy nsenter \
			cp /usr/bin/nsenter /output/usr/bin/nsenter; \
			\
			# Show what the binaries need \
			echo "Libraries needed by kata-deploy:"; \
			ldd /tmp/kata-deploy || echo "ldd failed"; \
			echo "Libraries needed by nsenter:"; \
			ldd /usr/bin/nsenter || echo "ldd failed"; \
			\
			# Extract and copy all library paths from both binaries \
			for binary in /tmp/kata-deploy /usr/bin/nsenter; do \
				echo "Processing ${binary}..."; \
				# Get libraries with "=>" (shared libs) \
				ldd "${binary}" 2>/dev/null | grep "=>" | awk '{print $3}' | sort -u | while read -r lib; do \
					if [ -n "${lib}" ] && [ -f "${lib}" ]; then \
						dest_dir="/output$(dirname "${lib}")"; \
						mkdir -p "${dest_dir}"; \
						cp -Ln "${lib}" "${dest_dir}/" 2>/dev/null || true; \
						echo "  Copied lib: ${lib}"; \
					fi; \
				done; \
			done; \
			\
			# Copy the dynamic linker - it's at /lib/ld64.so.1 (not /lib64/) \
			echo "Copying dynamic linker:"; \
			mkdir -p /output/lib; \
			cp -Ln /lib/ld64.so* /output/lib/ 2>/dev/null || true; \
			cp -Ln /lib64/ld64.so* /output/lib64/ 2>/dev/null || true; \
			\
			echo "glibc" > /output/.libc-type; \
			;; \
		*) \
			echo "amd64/arm64: will use musl-based static binaries"; \
			echo "musl" > /output/.libc-type; \
			# Create placeholder so COPY doesn't fail \
			touch /output/lib/.placeholder; \
			touch /output/lib64/.placeholder; \
			touch /output/usr/bin/.placeholder; \
			;; \
	esac

# Copy musl nsenter from alpine for amd64/arm64
COPY --from=artifact-extractor /usr/bin/nsenter /output/usr/bin/nsenter-musl
COPY --from=artifact-extractor /lib/ld-musl-*.so.1 /output/lib/

# For amd64/arm64, use the musl nsenter; for ppc64le/s390x, keep the glibc one
RUN \
	HOST_ARCH="$(uname -m)"; \
	case "${HOST_ARCH}" in \
		"x86_64"|"aarch64") \
			mv /output/usr/bin/nsenter-musl /output/usr/bin/nsenter; \
			;; \
		*) \
			rm -f /output/usr/bin/nsenter-musl; \
			;; \
	esac

#### kata-deploy main image
FROM gcr.io/distroless/static-debian13@sha256:972618ca78034aaddc55864342014a96b85108c607372f7cbd0dbd1361f1d841

ARG DESTINATION=/opt/kata-artifacts

# Copy extracted kata artifacts
COPY --from=artifact-extractor ${DESTINATION} ${DESTINATION}

# Copy Rust binary
COPY --from=rust-builder /kata-deploy/bin/kata-deploy /usr/bin/kata-deploy

# Copy nsenter and required libraries (assembled based on architecture)
COPY --from=runtime-assembler /output/usr/bin/nsenter /usr/bin/nsenter
COPY --from=runtime-assembler /output/lib/ /lib/
COPY --from=runtime-assembler /output/lib64/ /lib64/

# Copy nydus snapshotter
COPY tools/packaging/kata-deploy/nydus-snapshotter ${DESTINATION}/nydus-snapshotter
COPY --from=nydus-binary-downloader /opt/nydus-snapshotter/bin/containerd-nydus-grpc ${DESTINATION}/nydus-snapshotter/
COPY --from=nydus-binary-downloader /opt/nydus-snapshotter/bin/nydus-overlayfs ${DESTINATION}/nydus-snapshotter/

# Copy runtimeclasses and node-feature-rules
COPY tools/packaging/kata-deploy/node-feature-rules ${DESTINATION}/node-feature-rules

ENTRYPOINT ["/usr/bin/kata-deploy"]
