ARG VERSION=latest # BASE_IMAGE is the image produced by Dockerfile.native-package. Release and nightly # tags use VERSION; reusable builds override BASE_IMAGE with the digest-qualified # native image they just published. # # That image is ubi9-micro and has no package manager, so this image cannot extend it. ARG BASE_IMAGE=floci/floci:${VERSION} FROM ${BASE_IMAGE} AS native # Everything that does not depend on the Floci version. CI builds this stage alone # (target: tools) to test the aws CLI shim and to warm the layer cache. FROM registry.access.redhat.com/ubi9-minimal:9.7 AS tools LABEL org.opencontainers.image.title="Floci (compat)" \ org.opencontainers.image.description="Floci compatibility image with AWS CLI and boto3 pre-installed" \ org.opencontainers.image.url="https://floci.io" \ org.opencontainers.image.source="https://github.com/floci-io/floci" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.vendor="Floci" \ description="Floci compatibility image with AWS CLI and boto3 pre-installed" \ io.k8s.description="Floci compatibility image with AWS CLI and boto3 pre-installed" \ io.k8s.display-name="Floci (compat)" \ io.openshift.expose-services="4566:tcp" \ io.openshift.tags="floci aws-emulator" \ maintainer="Floci " \ name="floci/floci" \ summary="Floci compatibility image with AWS CLI and boto3 pre-installed" \ url="https://floci.io" \ vendor="Floci" ENV AWS_DEFAULT_REGION=us-east-1 ENV AWS_ACCESS_KEY_ID=test ENV AWS_SECRET_ACCESS_KEY=test ENV AWS_ENDPOINT_URL="http://localhost:4566" ENV AWS_CONFIG_FILE=/etc/floci/aws/config ENV AWS_PAGER="" # AWS CLI v2. v1 (the only version pip3 install awscli can give you) resolves # a fixed set of services (SQS, RDS, DocDB, Neptune, EMR, Health) through # awscli/customizations/overridesslcommonname.py, which forces the real AWS # endpoint whenever no --endpoint-url flag is passed on the command line — # no environment variable, including AWS_ENDPOINT_URL and the per-service # AWS_ENDPOINT_URL_ overrides, can redirect it (aws/aws-cli#8737). # v2 has no equivalent override and resolves every service through the # normal endpoint chain, so AWS_ENDPOINT_URL alone is sufficient. # # v1 also entered maintenance mode 2026-07-15 (EOL 2027-07-15), so this is # needed regardless of the endpoint-resolution bug. # # cli_binary_format has no env var in v2 (its provider chain is config-file, # then a base64 constant), so blob params default to base64 here, same as # a v2 install against real AWS. A hook passing raw bytes (e.g. # aws lambda invoke --payload) needs --cli-binary-format raw-in-base64-out. ARG AWSCLI_VERSION=2.36.24 RUN useradd -r -u 1001 -g root -d /app -s /sbin/nologin floci \ && microdnf install -y --nodocs python3 python3-pip unzip \ && pip3 install --no-cache-dir boto3 \ && case "$(uname -m)" in \ x86_64) arch=x86_64 ;; \ aarch64) arch=aarch64 ;; \ *) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;; \ esac \ && curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-${arch}-${AWSCLI_VERSION}.zip" -o /tmp/awscliv2.zip \ && unzip -q /tmp/awscliv2.zip -d /tmp \ && /tmp/aws/install --bin-dir /usr/bin --install-dir /usr/local/aws-cli \ && rm -rf /tmp/awscliv2.zip /tmp/aws \ # The CLI's `aws help` example pages are 26 MB of text nobody reads inside a container; # the command reference itself (dist/awscli/data) stays. unzip exists only to unpack the CLI. # pip stays: the image documents it and init hooks use it to install packages. && rm -rf /usr/local/aws-cli/v2/*/dist/awscli/examples \ # Tab completion has no user inside a container: aws_completer and its 21 MB ac.index go. # The system Python keeps pip, setuptools and what boto3 imports; the optimised .pyc # variants, lib2to3 and ensurepip (pip is already installed) are build-time extras. && rm -rf /usr/local/aws-cli/v2/*/dist/awscli/data/ac.index /usr/local/aws-cli/v2/*/dist/aws_completer /usr/bin/aws_completer \ && rm -rf /usr/lib64/python3.9/lib2to3 /usr/lib64/python3.9/ensurepip \ && python3 -c 'import pathlib; [p.unlink() for d in ("/usr/lib64/python3.9","/usr/lib/python3.9") for p in pathlib.Path(d).rglob("*.opt-[12].pyc")]' \ && microdnf remove -y unzip \ && microdnf clean all \ && rm -rf /root/.cache/pip COPY --chmod=755 bin/awslocal /usr/local/bin/awslocal # Pre-create the init-hook directories so archive-based copies (Testcontainers # withCopyFileToContainer / `docker cp`) can drop hooks straight into them. # Docker's archive extract unpacks into an existing dir and never mkdir -p's the # parents, so without these the copy fails. Mirrors LocalStack, which ships # /etc/localstack/init. Owned by the runtime user (uid 1001, group root). RUN mkdir -p /etc/localstack/init/boot.d /etc/localstack/init/start.d /etc/localstack/init/ready.d \ /etc/floci/init/boot.d /etc/floci/init/start.d /etc/floci/init/ready.d \ && chown -R 1001:root /etc/localstack /etc/floci WORKDIR /app # A chmod after the copy below would rewrite the 200 MB binary into a second layer. RUN chown 1001:root /app && chmod g+rwX /app FROM tools ARG VERSION ENV FLOCI_VERSION=${VERSION} ENV FLOCI_STORAGE_PERSISTENT_PATH=/app/data # The compatibility image can be built on top of an older native image during # CI. An environment variable is ignored by older binaries and enables the # network guard in images that support it. ENV FLOCI_SECURITY_ALLOW_UNSAFE_NETWORK_EXPOSURE=true COPY --from=native --chown=1001:root /app/ /app/ VOLUME /app/data COPY --from=native /usr/local/bin/docker-entrypoint.sh /usr/local/bin/localstack-parity.sh /usr/local/bin/ # From the build context, not the native image: a published native image may predate the script. COPY --chmod=755 docker/healthcheck.sh /usr/local/bin/ EXPOSE 4566 HEALTHCHECK --interval=5s --timeout=3s --retries=5 CMD ["/usr/local/bin/healthcheck.sh"] ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] # A published port needs 0.0.0.0 inside the container, which Floci only accepts with explicit consent. CMD ["/app/application", "-Dquarkus.http.host=0.0.0.0"] USER 1001