Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.git
.github
*.md
temp
6 changes: 0 additions & 6 deletions .github/workflows/kind-cats.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 0 additions & 6 deletions .github/workflows/kind-smoke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# renovate: datasource=docker depName=alpine
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
FROM alpine:3.23@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709

It's better to use the current Alpine version instead of the old 3.21 release.


# 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 \
Copy link
Copy Markdown
Contributor

@ZPascal ZPascal Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've asked myself: Is ash (the default Alpine shell) not enough for this case? Is bash really necessary?

If yes, we should also add the bash-completion package to handle the bash autocompletion.

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"]
82 changes: 57 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
37 changes: 24 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <https://gnuwin32.sourceforge.net/packages/make.htm>
All other tools (kind, kubectl, helm, helmfile) are bundled in the installer container.

## Run the Installation

Expand Down Expand Up @@ -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

Expand Down
51 changes: 27 additions & 24 deletions scripts/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"