From 602d10774c2fc362ad3bd2193d1eb8658867cc08 Mon Sep 17 00:00:00 2001 From: Florian Braun <5863788+FloThinksPi@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:06:09 +0100 Subject: [PATCH] Add self-contained installer container Introduce a Docker-based installation approach that bundles all required tooling (kind, kubectl, helm, helmfile, cf CLI) in a single container image. Users now only need Docker installed on their host. Changes: - Add Dockerfile with Alpine base and all CLI tools - Add .dockerignore to keep image minimal - Refactor Makefile to run commands inside container - Mount temp/ and docker socket for cluster management - Update README with simplified prerequisites and usage - Modify init.sh to use native openssl/ssh-keygen (available in container) The original make targets (up, down, login, bootstrap) work as before, but now automatically build and use the installer container. Internal targets prefixed with _ can still be used directly if tools are installed on the host. --- .dockerignore | 4 ++ .github/workflows/kind-cats.yaml | 6 --- .github/workflows/kind-smoke.yaml | 6 --- Dockerfile | 51 +++++++++++++++++++ Makefile | 82 +++++++++++++++++++++---------- README.md | 37 +++++++++----- scripts/init.sh | 51 ++++++++++--------- 7 files changed, 163 insertions(+), 74 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..dcd097ec --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git +.github +*.md +temp \ No newline at end of file diff --git a/.github/workflows/kind-cats.yaml b/.github/workflows/kind-cats.yaml index a9e77094..ca74816a 100644 --- a/.github/workflows/kind-cats.yaml +++ b/.github/workflows/kind-cats.yaml @@ -23,16 +23,10 @@ jobs: - name: Install dependencies run: | mkdir -p $HOME/.local/bin && echo "$HOME/.local/bin" >> "$GITHUB_PATH" - curl -L https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-amd64 -o $HOME/.local/bin/kind - chmod +x $HOME/.local/bin/kind - curl -L https://github.com/helmfile/helmfile/releases/download/v${HELMFILE_VERSION}/helmfile_${HELMFILE_VERSION}_linux_amd64.tar.gz | tar -zx - mv helmfile $HOME/.local/bin/helmfile curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${CF_CLI_VERSION}&source=github" | tar -zx mv cf8 $HOME/.local/bin/cf env: CF_CLI_VERSION: '8.14.1' - KIND_VERSION: '0.30.0' - HELMFILE_VERSION: '1.2.3' - name: Run make up env: ENABLE_NFS_VOLUME: "true" diff --git a/.github/workflows/kind-smoke.yaml b/.github/workflows/kind-smoke.yaml index 2fe433e0..4d2b027b 100644 --- a/.github/workflows/kind-smoke.yaml +++ b/.github/workflows/kind-smoke.yaml @@ -42,16 +42,10 @@ jobs: if: steps.check_changes.outputs.skip != 'true' run: | mkdir -p $HOME/.local/bin && echo "$HOME/.local/bin" >> "$GITHUB_PATH" - curl -L https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-amd64 -o $HOME/.local/bin/kind - chmod +x $HOME/.local/bin/kind - curl -L https://github.com/helmfile/helmfile/releases/download/v${HELMFILE_VERSION}/helmfile_${HELMFILE_VERSION}_linux_amd64.tar.gz | tar -zx - mv helmfile $HOME/.local/bin/helmfile curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${CF_CLI_VERSION}&source=github" | tar -zx mv cf8 $HOME/.local/bin/cf env: CF_CLI_VERSION: '8.14.1' - KIND_VERSION: '0.30.0' - HELMFILE_VERSION: '1.2.3' - name: Run make up if: steps.check_changes.outputs.skip != 'true' run: make up diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0e0e4138 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +# renovate: datasource=docker depName=alpine +FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709 + +# renovate: datasource=github-releases depName=kubernetes-sigs/kind +ARG KIND_VERSION=v0.31.0 +# renovate: datasource=github-releases depName=kubernetes/kubernetes +ARG KUBECTL_VERSION=v1.35.1 +# renovate: datasource=github-releases depName=helmfile/helmfile +ARG HELMFILE_VERSION=v1.3.2 +# renovate: datasource=github-releases depName=helm/helm +ARG HELM_VERSION=v4.1.1 +# renovate: datasource=github-releases depName=cloudfoundry/cli +ARG CF_CLI_VERSION=v8.17.0 + +RUN apk add --no-cache \ + bash \ + curl \ + docker-cli \ + docker-cli-compose \ + jq \ + make \ + openssh-keygen \ + openssl + +ARG TARGETARCH + +RUN curl -fsSL "https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-${TARGETARCH}" -o /usr/local/bin/kind \ + && chmod +x /usr/local/bin/kind + +RUN curl -fsSL "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" -o /usr/local/bin/kubectl \ + && chmod +x /usr/local/bin/kubectl + +RUN curl -fsSL "https://get.helm.sh/helm-${HELM_VERSION}-linux-${TARGETARCH}.tar.gz" | tar xz -C /tmp \ + && mv /tmp/linux-${TARGETARCH}/helm /usr/local/bin/helm \ + && rm -rf /tmp/linux-${TARGETARCH} + +RUN curl -fsSL "https://github.com/helmfile/helmfile/releases/download/${HELMFILE_VERSION}/helmfile_${HELMFILE_VERSION#v}_linux_${TARGETARCH}.tar.gz" | tar xz -C /tmp \ + && mv /tmp/helmfile /usr/local/bin/helmfile \ + && rm -rf /tmp/LICENSE /tmp/README* + +RUN CF_ARCH=$([ "${TARGETARCH}" = "amd64" ] && echo "x86-64" || echo "${TARGETARCH}") \ + && curl -fsSL "https://github.com/cloudfoundry/cli/releases/download/${CF_CLI_VERSION}/cf8-cli_${CF_CLI_VERSION#v}_linux_${CF_ARCH}.tgz" | tar xz -C /tmp \ + && mv /tmp/cf8 /usr/local/bin/cf \ + && rm -rf /tmp/LICENSE /tmp/NOTICE + +WORKDIR /workspace + +COPY . . + +ENTRYPOINT ["/bin/bash", "-c"] +CMD ["make"] diff --git a/Makefile b/Makefile index 5cbbfed0..1cc95b83 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,79 @@ LOCAL = true TARGET_ARCH ?= $(if $(filter true,$(LOCAL)),$(shell go env GOARCH),amd64) -# renovate: dataSource=github-releases depName=helmfile/helmfile -HELMFILE_VERSION ?= "1.3.2" +IMAGE ?= cf-kind-deployment:latest +DOCKER_SOCKET ?= $(shell docker context inspect --format '{{.Endpoints.docker.Host}}' | sed 's|unix://||') +TTY_FLAG := $(shell [ -t 0 ] && echo "-it" || echo "-i") -init: temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env +# Common docker run options +run_opts = --rm $(TTY_FLAG) \ + --network host \ + -v $(DOCKER_SOCKET):/var/run/docker.sock \ + -v "$$PWD/temp:/workspace/temp" \ + -e ENABLE_TCP_ROUTING \ + -e ENABLE_NFS_VOLUME \ + -e ENABLE_POLICY_SUPPORT \ + -e ENABLE_LOGGREGATOR \ + -e DISABLE_CACHE -temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env: - @ ./scripts/init.sh +# Container run command +run = docker run $(run_opts) $(IMAGE) -install: - kind get kubeconfig --name cfk8s > temp/kubeconfig - docker run --rm --net=host --env-file temp/secrets.env \ - --env ENABLE_TCP_ROUTING \ - --env ENABLE_NFS_VOLUME \ - --env ENABLE_POLICY_SUPPORT \ - --env ENABLE_LOGGREGATOR \ - -v "$$PWD/temp/certs:/certs" -v "$$PWD/temp/kubeconfig:/helm/.kube/config:ro" -v "$$PWD:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v$(HELMFILE_VERSION) helmfile sync +# Default targets (container-based) +up: _build-installer-quiet + $(run) "make _create-kind _init _install" + +down: _build-installer-quiet + $(run) "make _delete-kind" + rm -rf temp login: - @ . temp/secrets.sh; \ + @ . ./temp/secrets.sh; \ cf login -a https://api.127-0-0-1.nip.io -u ccadmin -p "$$CC_ADMIN_PASSWORD" --skip-ssl-validation -create-kind: +bootstrap: _build-installer-quiet + $(run) "make login _bootstrap" + +bootstrap-complete: _build-installer-quiet + $(run) "make login _bootstrap-complete" + +shell: _build-installer-quiet + docker run $(run_opts) -v "$$PWD:/workspace" $(IMAGE) "bash" + +# Build the installer container (verbose) +build-installer: + docker build -t $(IMAGE) . + +# Build the installer container (quiet) +_build-installer-quiet: + @docker build -q -t $(IMAGE) . > /dev/null + +# Internal targets (run inside container or on host with tools installed) +_init: temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env + +temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env: + @ ./scripts/init.sh + +_install: + kind get kubeconfig --name cfk8s > temp/kubeconfig + @ . ./temp/secrets.sh && KUBECONFIG=temp/kubeconfig helmfile sync + +_create-kind: @ ./scripts/create-kind.sh -delete-kind: +_delete-kind: @ ./scripts/delete-kind.sh -create-org: +_create-org: cf create-org test cf create-space -o test test cf target -o test -s test @ ./scripts/set_feature_flags.sh -bootstrap: create-org +_bootstrap: _create-org @ ./scripts/upload_buildpacks.sh -bootstrap-complete: create-org +_bootstrap-complete: _create-org @ ALL_BUILDPACKS=true ./scripts/upload_buildpacks.sh -up: create-kind init install - -down: delete-kind - @ rm -rf temp - -PHONY: install login create-kind delete-kind up down create-org bootstrap bootstrap-complete +.PHONY: up down login bootstrap bootstrap-complete shell build-installer +.PHONY: _init _install _create-kind _delete-kind _create-org _bootstrap _bootstrap-complete _build-installer-quiet diff --git a/README.md b/README.md index 5e601ae5..7bac5181 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,10 @@ This repository provides a simple and fast way to run Cloud Foundry locally. It ## Prerequisites -The following tools need to be installed: +- [Docker](https://docs.docker.com/engine/install/) (with Docker Compose), alternatives like colima or podman may also work. +- [`cf` CLI](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html) (v8.17.0+) -- [`docker`](https://docs.docker.com/engine/install/) -- [`kind`](https://kind.sigs.k8s.io/docs/user/quick-start/#installing-from-release-binaries) (v0.31.0 or higher) -- [`kubectl`](https://kubernetes.io/docs/tasks/tools/#kubectl) (v1.35.1 or higher) -- `make`: - - It should be already installed on MacOS and Linux. - - For Windows installation see: +All other tools (kind, kubectl, helm, helmfile) are bundled in the installer container. ## Run the Installation @@ -46,12 +42,27 @@ make down You can configure the installation by setting following environment variables: -| environment variable | default | component(s) to be installed | -| ----------------------- | ------- | ---------------------------------------------------------------------- | -| `ENABLE_LOGGREGATOR` | `true` | Loggregator | -| `ENABLE_POLICY_SUPPORT` | `true` | policy-serverver, policy-agent, bosh-dns, service-discovery-controller | -| `ENABLE_TCP_ROUTING` | `true` | cf-tcp-router, routing-api | -| `ENABLE_NFS_VOLUME` | `false` | nfsbroker | +| Environment Variable | Default | Description | +|---------------------|---------|-------------| +| `ENABLE_LOGGREGATOR` | `true` | Install Loggregator | +| `ENABLE_POLICY_SUPPORT` | `true` | Install policy-server, policy-agent, bosh-dns, service-discovery-controller | +| `ENABLE_TCP_ROUTING` | `true` | Install cf-tcp-router, routing-api | +| `ENABLE_NFS_VOLUME` | `false` | Install nfsbroker | +| `DISABLE_CACHE` | `false` | Disable registry pull-through caches | +| `DOCKER_SOCKET` | auto-detected | Path to Docker socket (override if auto-detection fails) | + +Example: + +```bash +ENABLE_NFS_VOLUME=true make up +``` + +## Additional Commands + +| Command | Description | +|---------|-------------| +| `make shell` | Open a development shell (mounts local source code for development/testing) | +| `make build-installer` | Build the installer container without running it | ## Unsupported Features diff --git a/scripts/init.sh b/scripts/init.sh index ea2b4bcd..1ec80682 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -2,27 +2,30 @@ set -euo pipefail -mkdir -p temp/certs - -OPENSSL="docker run --rm -v $(pwd)/temp/certs:/certs -v $(pwd)/certs/all-in-one.conf:/all-in-one.conf alpine/openssl" -SSH_KEYGEN="docker run --rm -v $(pwd)/temp/certs:/certs --entrypoint /usr/bin/ssh-keygen linuxserver/openssh-server" - -$OPENSSL genrsa -traditional -out /certs/ca.key 4096 -$OPENSSL req -x509 -key /certs/ca.key -out /certs/ca.crt -days 365 -noenc -subj "/CN=ca/O=ca" \ - -config /all-in-one.conf -extensions v3_ca > /dev/null 2>&1 -$OPENSSL req -new -keyout /certs/all-in-one.key -out /certs/all-in-one.csr -noenc -config /all-in-one.conf > /dev/null 2>&1 -$OPENSSL x509 -req -in /certs/all-in-one.csr -CA /certs/ca.crt -CAkey /certs/ca.key -CAcreateserial \ - -out /certs/all-in-one.crt -days 365 -copy_extensions copy > /dev/null 2>&1 - -rm -f temp/certs/ssh_key temp/certs/ssh_key.pub -$SSH_KEYGEN -t rsa -b 4096 -f /certs/ssh_key -N "" > /dev/null 2>&1 - -echo "export BLOBSTORE_PASSWORD=$($OPENSSL rand -hex 16)" > temp/secrets.sh -echo "export DB_PASSWORD=$($OPENSSL rand -hex 16)" >> temp/secrets.sh -echo "export OAUTH_CLIENTS_SECRET=$($OPENSSL rand -hex 16)" >> temp/secrets.sh -echo "export DIEGO_SSH_CREDENTIALS=$($OPENSSL rand -hex 16)" >> temp/secrets.sh -echo "export CC_ADMIN_PASSWORD=$($OPENSSL rand -hex 16)" >> temp/secrets.sh -echo "export UAA_ADMIN_SECRET=$($OPENSSL rand -hex 16)" >> temp/secrets.sh -echo "export SSH_PROXY_KEY_FINGERPRINT=$($SSH_KEYGEN -l -E md5 -f /certs/ssh_key.pub | cut -d' ' -f2 | cut -d: -f2-)" >> temp/secrets.sh - -sed 's/^export //g' temp/secrets.sh > temp/secrets.env +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +mkdir -p "${ROOT_DIR}/temp/certs" + +CERTS_DIR="${ROOT_DIR}/temp/certs" +CONF_FILE="${ROOT_DIR}/certs/all-in-one.conf" + +openssl genrsa -traditional -out "${CERTS_DIR}/ca.key" 4096 +openssl req -x509 -key "${CERTS_DIR}/ca.key" -out "${CERTS_DIR}/ca.crt" -days 365 -noenc -subj "/CN=ca/O=ca" \ + -config "${CONF_FILE}" -extensions v3_ca > /dev/null 2>&1 +openssl req -new -keyout "${CERTS_DIR}/all-in-one.key" -out "${CERTS_DIR}/all-in-one.csr" -noenc -config "${CONF_FILE}" > /dev/null 2>&1 +openssl x509 -req -in "${CERTS_DIR}/all-in-one.csr" -CA "${CERTS_DIR}/ca.crt" -CAkey "${CERTS_DIR}/ca.key" -CAcreateserial \ + -out "${CERTS_DIR}/all-in-one.crt" -days 365 -copy_extensions copy > /dev/null 2>&1 + +rm -f "${CERTS_DIR}/ssh_key" "${CERTS_DIR}/ssh_key.pub" +ssh-keygen -t rsa -b 4096 -f "${CERTS_DIR}/ssh_key" -N "" > /dev/null 2>&1 + +echo "export BLOBSTORE_PASSWORD=$(openssl rand -hex 16)" > "${ROOT_DIR}/temp/secrets.sh" +echo "export DB_PASSWORD=$(openssl rand -hex 16)" >> "${ROOT_DIR}/temp/secrets.sh" +echo "export OAUTH_CLIENTS_SECRET=$(openssl rand -hex 16)" >> "${ROOT_DIR}/temp/secrets.sh" +echo "export DIEGO_SSH_CREDENTIALS=$(openssl rand -hex 16)" >> "${ROOT_DIR}/temp/secrets.sh" +echo "export CC_ADMIN_PASSWORD=$(openssl rand -hex 16)" >> "${ROOT_DIR}/temp/secrets.sh" +echo "export UAA_ADMIN_SECRET=$(openssl rand -hex 16)" >> "${ROOT_DIR}/temp/secrets.sh" +echo "export SSH_PROXY_KEY_FINGERPRINT=$(ssh-keygen -l -E md5 -f "${CERTS_DIR}/ssh_key.pub" | cut -d' ' -f2 | cut -d: -f2-)" >> "${ROOT_DIR}/temp/secrets.sh" + +sed 's/^export //g' "${ROOT_DIR}/temp/secrets.sh" > "${ROOT_DIR}/temp/secrets.env"