FROM oven/bun:latest@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895

# This Dockerfile produces a *vanilla* management image — bun + unzip +
# bunfig, no celilo. `cele2e build-infra` runs install.sh inside a
# container started from this image and `docker commit`s the result onto
# the `celilo-e2e/management:latest` tag, so every downstream test boots
# a management container that was itself provisioned by install.sh.
#
# The vanilla tag survives the bake — the install-sh regression test
# (packages/e2e/tests/install-sh.test.ts) uses it to exercise install.sh
# end-to-end on every run.
#
# Source-mount dev loop (mount /celilo into a running container to use
# workspace source instead of the baked celilo) is provided by the
# shim that the bake step writes — see bin/e2e-bake-management.

# Make apt resilient to flaky upstream (no apt-cacher-ng masking transient errors)
COPY config/apt/99retries.conf /etc/apt/apt.conf.d/99retries.conf

RUN apt-get update && apt-get install -y \
    openssh-client \
    # openssh-server: the celilo-bootstrap deb Depends on it (remote-API server).
    # Pre-caching it here keeps the bootstrap-apt foreground `apt install` fast
    # (no download over the bumped Squid path) and mirrors reality — a real
    # management box already runs sshd. The deb still owns the celilo-api account
    # + forced-command authorized_keys; this only pre-stages the package.
    openssh-server \
    ansible \
    iproute2 \
    iputils-ping \
    net-tools \
    dnsutils \
    curl \
    unzip \
    procps \
    zsh \
    gnupg \
    # The hook jail (openspec/changes/hook-process-boundary, stage 2) runs every
    # hook under bubblewrap. Without it the management box runs hooks unjailed,
    # which is a valid mode but not the one the e2e suite is there to exercise.
    # The container needs security_opt relaxations to let it build a namespace —
    # see the measured ladder in docker-compose-generator.ts.
    bubblewrap \
    && rm -rf /var/lib/apt/lists/*

# --- Terraform, plus an OFFLINE provider mirror -------------------------------
#
# celilo runs `terraform apply` on the management box for any module placed on a
# container_service. Until the Proxmox simulator existed, no suite could reach
# that path — every test used the machine pool, where there is no infrastructure
# to create — so the rig never needed terraform at all, and the deploy failed
# with a bare `/bin/sh: 1: terraform: not found`.
#
# The provider is MIRRORED into the image rather than fetched at deploy time.
# `terraform init` would otherwise reach registry.terraform.io, and the rig
# simulates the whole internet: that name does not resolve inside the topology,
# and making it resolve would mean pointing a public name at a private address —
# the exact model violation the e2e rules prohibit. A filesystem mirror is how
# real airgapped installs do this, so the rig is if anything more faithful.
#
# Version pinned to what the module templates declare. If a module ever moves off
# `telmate/proxmox@3.0.2-rc07`, `terraform init` fails in the rig with a clear
# "provider not available in mirror" rather than silently reaching outward.
ARG TERRAFORM_VERSION=1.7.1
ARG PROXMOX_PROVIDER_VERSION=3.0.2-rc07
RUN set -eux; \
    arch="$(dpkg --print-architecture)"; \
    case "$arch" in \
      amd64) tf_arch=amd64 ;; \
      arm64) tf_arch=arm64 ;; \
      *) echo "unsupported arch: $arch" >&2; exit 1 ;; \
    esac; \
    curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${tf_arch}.zip" -o /tmp/terraform.zip; \
    unzip -q /tmp/terraform.zip -d /usr/local/bin; \
    rm /tmp/terraform.zip; \
    terraform version

# Populate the mirror for the platform this image runs on.
RUN set -eux; \
    mkdir -p /opt/terraform-mirror /tmp/tfmirror; \
    cd /tmp/tfmirror; \
    printf 'terraform {\n  required_providers {\n    proxmox = {\n      source  = "telmate/proxmox"\n      version = "%s"\n    }\n  }\n}\n' "${PROXMOX_PROVIDER_VERSION}" > main.tf; \
    terraform providers mirror /opt/terraform-mirror; \
    rm -rf /tmp/tfmirror

# `direct` is excluded on purpose: if the mirror is missing a provider we want a
# loud failure, not a silent reach for the real internet from inside a simulated
# one.
RUN printf 'provider_installation {\n  filesystem_mirror {\n    path    = "/opt/terraform-mirror"\n    include = ["registry.terraform.io/*/*"]\n  }\n  direct {\n    exclude = ["registry.terraform.io/*/*"]\n  }\n}\n' > /etc/terraform.rc
ENV TF_CLI_CONFIG_FILE=/etc/terraform.rc

# Set zsh as default shell
RUN chsh -s /bin/zsh root

# Trust the E2E forward proxy CA (for transparent HTTPS interception)
COPY config/proxy/squid-ca.crt /usr/local/share/ca-certificates/e2e-proxy-ca.crt
# Trust Pebble's test CA (so bun fetch() trusts certs issued by the simulated Let's Encrypt)
COPY config/pebble/pebble-ca.crt /usr/local/share/ca-certificates/pebble-ca.crt
# Trust the OFF-FLEET cPanel host sim's CA. That host's cert is PRE-INSTALLED
# (as a real shared host's AutoSSL cert is) rather than issued by Pebble —
# external_web never owns TLS — so its CA is a separate, static trust anchor.
# external_web's authoritative post-publish check HTTPS-GETs the route from
# here, which is why mgmt in particular must trust it.
COPY config/cpanel-host/cpanel-host-ca.crt /usr/local/share/ca-certificates/cpanel-host-ca.crt
# Trust the Namecheap DDNS sim's CA. Its cert is likewise PRE-INSTALLED rather
# than Pebble-issued: celilo does not own a third-party API's TLS. This is what
# lets namecheap's hooks call the REAL endpoint URL over real TLS, instead of
# being redirected at an http://ip:port by an environment variable that an
# allow-listed hook environment no longer carries.
COPY config/namecheap/namecheap-ca.crt /usr/local/share/ca-certificates/namecheap-ca.crt
RUN update-ca-certificates

# Tell bun (and Node-compat code) to merge the system CA bundle with
# its built-in Mozilla bundle. Without this, `bun fetch()` ignores
# /etc/ssl/certs/ca-certificates.crt (and therefore the Pebble runtime
# root that management-routes.sh installs at startup) and rejects any
# certs Pebble issues — including those for the test domains caddy
# serves. Confirmed bitten 2026-05-06: curl worked but bun didn't.
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt

# Bun scope override: route `@celilo/*` to the simulated npm registry
# (npm-registry-sim at npm-registry.lab) so install.sh's `bun add -g
# @celilo/cli` step works without reaching real npmjs.org. Other scopes
# fall through to Bun's normal resolution.
RUN printf '[install.scopes]\n"@celilo" = "http://npm-registry.lab"\n' > /root/.bunfig.toml

# Standard modules pre-packaged as .netapp files — built by e2e-build.
# Tests import from /netapps/<module>.netapp instead of the monorepo mount.
COPY netapps/ /netapps/

COPY config/ssh/generate-keys.sh /config/ssh/generate-keys.sh
COPY config/routing/management-routes.sh /startup.sh
RUN chmod +x /startup.sh /config/ssh/generate-keys.sh

WORKDIR /root
CMD ["/startup.sh"]
