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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ config/settings.yml
releases/*.tgz
releases/**/*.tgz
ci/scripts/stemcell/*.tgz
ci/scripts/stemcell-bionic/*.tgz
ci/scripts/stemcell-jammy/*.tgz
dev_releases
blobs/*
.blobs
Expand Down
73 changes: 14 additions & 59 deletions acceptance-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,66 +14,11 @@ cd acceptance-tests

### Running on Docker for Mac

Unfortunately rosetta on arm based macs doesn't support docker in docker required for bosh docker-cpi used in this test setup. You will either have to run on x86 based mac or some remote x86 workstation. Virtualization using QEMU is possible, but so slow the tests fail on timeouts.
Acceptance tests cannot be run on Mac with arm64 architecture:
* Docker-in-Docker, which is required by this test setup, is not supported on arm64.
* The `ghcr.io/cloudfoundry/bosh/docker-cpi` image is only built for x86 and will not run on arm64.

The BOSH Docker CPI requires cgroups v1 to be active. Docker for Mac since 4.3.x uses cgroups v2 by default.

v1 can be restored with the flag `deprecatedCgroupv1` to `true` in `~/Library/Group Containers/group.com.docker/settings.json`.

A convenience script that does this for you is below.

**WARNING:** This will restart your Docker Desktop!

```shell
docker_restart_with_cgroupsv1() {
SETTINGS=~/Library/Group\ Containers/group.com.docker/settings.json

if ! command -v jq >/dev/null || ! command -v sponge; then
echo "Requires jq and sponge. Consider installing via:"
echo " brew install jq moreutils"
return
fi

cgroupsV1Enabled=$(jq '.deprecatedCgroupv1' "$SETTINGS")
if [ "$cgroupsV1Enabled" = "true" ]; then
echo "deprecatedCgroupv1 is already set to 'true'. Acceptance tests should work."
else
echo "Stopping Docker to set the config flag deprecatedCgroupv1 = true in $SETTINGS"

while docker ps -q 2>/dev/null; do
launchctl stop $(launchctl list | grep docker.docker | awk '{print $3}')
osascript -e 'quit app "Docker"'
echo "Waiting for Docker daemon to stop responding."
sleep 1
done
echo 'Setting "deprecatedCgroupv1" to true.'

# Add the needed cgroup config to docker settings.json
# sponge is needed because we're updating the same file in place
echo '{"deprecatedCgroupv1": true}' |
jq -s '.[0] * .[1]' "$SETTINGS" - |
sponge "$SETTINGS"
# Restart docker desktop
echo "Restarting Docker"
open --background -a Docker

while ! docker ps -q 2>/dev/null; do
echo "Waiting for Docker daemon to be back up again. Sleeping 1s."
sleep 1
done
fi

docker info | grep "Cgroup"
}

docker_restart_with_cgroupsv1
```

The output at the end should be:
```plain
Cgroup Driver: cgroupfs
Cgroup Version: 1
```
You will need to use an x86-based Mac or a remote x86 workstation instead. Virtualization via QEMU is possible, but is too slow in practice — tests will fail on timeouts.

### Focussed Tests

Expand All @@ -100,6 +45,16 @@ However, if you want to run exactly one specific test, make sure you pass the ex

Running tests in focus will also preserve the bosh container running after tests complete, so that you can easily run tests again without having to wait for bosh set-up again.

### Parallelism

By default, Ginkgo runs the test suite with smart parallelism (`-p`), automatically choosing the number of parallel nodes based on the available CPU count. You can override this with the `-P` flag:

```shell
./run-local.sh -P 4
```

This sets the number of Ginkgo parallel nodes to `4`. Set it to `1` to run tests sequentially, which can be useful for debugging flaky tests or when the host machine has limited resources.

### Persistent BOSH

Because BOSH setup takes a while (it starts from scratch with bosh create-env), it is useful to preserve the container with bosh already configured to run tests. This can be done either by providing test focus as described above, or `-k` (keep) switch to `run-local.sh` and `run-shell.sh` scripts, e.g. `run-shell.sh -k`. Once initial setup is complete, scripts will output a message about how to get back into the running container:
Expand Down
7 changes: 0 additions & 7 deletions acceptance-tests/acceptance_tests_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,3 @@ func checkNetOpErr(err error, expectString string) {
Expect(errors.As(tlsErr, &opErr)).To(BeTrue())
Expect(opErr.Err.Error()).To(ContainSubstring(expectString))
}

func writeLog(s string) {
ginkgoConfig, _ := GinkgoConfiguration()
for _, line := range strings.Split(s, "\n") {
fmt.Printf("node %d/%d: %s\n", ginkgoConfig.ParallelProcess, ginkgoConfig.ParallelTotal, line)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ import (
. "github.com/onsi/ginkgo/v2"
)

var _ = Describe("Bionic", func() {
It("Correctly proxies HTTP requests when using the Bionic stemcell", func() {
var _ = Describe("Jammy", func() {
It("Correctly proxies HTTP requests when using the Jammy stemcell", func() {

opsfileBionic := `---
# Configure Bionic stemcell
opsfileJammy := `---
# Configure Jammy stemcell
- type: replace
path: /stemcells/alias=default/os
value: ubuntu-bionic
value: ubuntu-jammy
`

haproxyBackendPort := 12000
haproxyInfo, _ := deployHAProxy(baseManifestVars{
haproxyBackendPort: haproxyBackendPort,
haproxyBackendServers: []string{"127.0.0.1"},
deploymentName: deploymentNameForTestNode(),
}, []string{opsfileBionic}, map[string]interface{}{}, true)
}, []string{opsfileJammy}, map[string]interface{}{}, true)

closeLocalServer, localPort := startDefaultTestServer()
defer closeLocalServer()
Expand Down
15 changes: 15 additions & 0 deletions acceptance-tests/log_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package acceptance_tests

import (
"fmt"
"strings"

. "github.com/onsi/ginkgo/v2"
)

func writeLog(s string) {
ginkgoConfig, _ := GinkgoConfiguration()
for _, line := range strings.Split(s, "\n") {
fmt.Printf("node %d/%d: %s\n", ginkgoConfig.ParallelProcess, ginkgoConfig.ParallelTotal, line)
}
}
79 changes: 40 additions & 39 deletions acceptance-tests/run-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@
set -eu
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
source "${REPO_DIR}/ci/scripts/functions-ci.sh"
FOCUS=""
PARALLELISM=""
KEEP_RUNNING=""

usage() {
echo -e "Usage: $0 [-F <ginkgo focus target>] [-k]
echo -e "Usage: $0 [-F <ginkgo focus target>] [-P <ginkgo nodes>] [-k]

-F Focus on a particular test. Expects a Ginkgo test name. Keep bosh running afterwards.
-P Set Ginkgo parallel node count. Default is '-p' (smart parallelism).
-k Keep bosh container running. Useful for debug." 1>&2; exit 1;
}

while getopts ":F:k" o; do
while getopts ":F:P:k" o; do
case "${o}" in
F)
FOCUS=${OPTARG}
KEEP_RUNNING=true
;;
P)
PARALLELISM=${OPTARG}
;;
k)
KEEP_RUNNING=true
;;
Expand All @@ -28,63 +34,58 @@ while getopts ":F:k" o; do
done
shift $((OPTIND-1))

docker_mac_check_cgroupsv1() {
# Force cgroups v1 on Docker for Mac
# inspired by https://github.com/docker/for-mac/issues/6073#issuecomment-1018793677

SETTINGS=~/Library/Group\ Containers/group.com.docker/settings.json

cgroupsV1Enabled=$(jq '.deprecatedCgroupv1' "$SETTINGS")
if [ "$cgroupsV1Enabled" != "true" ]; then
echo "deprecatedCgroupv1 should be enabled in $SETTINGS. Otherwise the acceptance tests will not run on Docker for Mac."
echo "Check in the README.md for a convenient script to set deprecatedCgroupv1 and restart Docker."
exit 1
fi
}

check_required_files() {
PIDS=""
REQUIRED_FILE_PATTERNS=(
ci/scripts/stemcell/bosh-stemcell-*-ubuntu-jammy-*.tgz!https://bosh.io/d/stemcells/bosh-warden-boshlite-ubuntu-jammy-go_agent
ci/scripts/stemcell-bionic/bosh-stemcell-*-ubuntu-bionic-*.tgz!https://bosh.io/d/stemcells/bosh-warden-boshlite-ubuntu-bionic-go_agent
ci/scripts/stemcell/bosh-stemcell-*-ubuntu-noble.tgz!https://bosh.io/d/stemcells/bosh-warden-boshlite-ubuntu-noble
ci/scripts/stemcell-jammy/bosh-stemcell-*-ubuntu-jammy-*.tgz!https://bosh.io/d/stemcells/bosh-warden-boshlite-ubuntu-jammy-go_agent
)

for entry in "${REQUIRED_FILE_PATTERNS[@]}"; do
pattern=$(cut -f1 -d! <<<"$entry")
url=$(cut -f2 -d! <<<"$entry")
folder=$(realpath "$(dirname "$REPO_DIR/$pattern")")
filepattern=$(basename "$pattern")
pattern=$folder/$filepattern

# shellcheck disable=SC2086
# glob resolution is desired here.
if [ -f $pattern ]; then
continue
fi

(
echo "$filepattern not found, downloading latest."
cd "$folder" && \
resolved=$(curl -s --write-out '\n%{redirect_url}' "$url" | tail -n1) && \
curl -s --remote-name --remote-header-name --location "$resolved" && \
echo "Downloaded '$url' successfully." && \
ls -1lh "$folder/"$filepattern
# Resolve the redirect URL to get the actual remote filename
resolved=$(curl -s --write-out '\n%{redirect_url}' "$url" | tail -n1 | tr -d '\n')
if [ -z "${resolved}" ]; then
echo "ERROR: could not resolve redirect URL for $url" >&2
exit 1
fi
remote_filename=$(basename "${resolved%%\?*}") # strip query string if any
echo "Remote file: ${remote_filename}" >&2

# Check what is already in the folder
existing=$(find "${folder}" -maxdepth 1 -name "${filepattern}" 2>/dev/null | head -1)

if [ -n "${existing}" ]; then
existing_filename=$(basename "${existing}")
if [ "${existing_filename}" = "${remote_filename}" ]; then
echo "${existing_filename} is up to date, skipping download." >&2
exit 0
fi
echo "Version changed: ${existing_filename} -> ${remote_filename}, deleting old file." >&2
rm -f "${existing}"
fi

echo "Downloading ${remote_filename} ..." >&2
cd "${folder}" && \
curl -s --remote-name --remote-header-name --location "${resolved}" && \
echo "Downloaded '${remote_filename}' successfully." >&2 && \
ls -1lh "${folder}/${remote_filename}" >&2
)&

PIDS="$PIDS $!"

done

# shellcheck disable=SC2086
# expansion is desired, as $PIDS is a list of PIDs. Wait on all of those PIDs.
wait $PIDS
}

check_required_files

if [ "$(uname)" == "Darwin" ]; then
docker_mac_check_cgroupsv1
fi

build_image "${REPO_DIR}/ci"
prepare_docker_scratch

Expand All @@ -93,9 +94,9 @@ if [ -n "$KEEP_RUNNING" ] ; then
echo
echo "*** KEEP_RUNNING enabled. Please clean up docker scratch after removing containers: ${DOCKER_SCRATCH}"
echo
docker run --privileged -v "$REPO_DIR":/repo -v "${DOCKER_SCRATCH}":/scratch/docker -e REPO_ROOT=/repo -e FOCUS="$FOCUS" -e KEEP_RUNNING="${KEEP_RUNNING}" haproxy-boshrelease-testflight bash -c "cd /repo/ci/scripts && ./acceptance-tests ; sleep infinity"
docker run --privileged -v "$REPO_DIR":/repo -v "${DOCKER_SCRATCH}":/scratch/docker -e REPO_ROOT=/repo -e FOCUS="${FOCUS}" -e PARALLELISM="${PARALLELISM}" -e KEEP_RUNNING="${KEEP_RUNNING}" haproxy-boshrelease-testflight bash -c "cd /repo/ci/scripts && ./acceptance-tests ; sleep infinity"
else
docker run --rm --privileged -v "$REPO_DIR":/repo -v "${DOCKER_SCRATCH}":/scratch/docker -e REPO_ROOT=/repo -e KEEP_RUNNING="" haproxy-boshrelease-testflight bash -c "cd /repo/ci/scripts && ./acceptance-tests"
docker run --rm --privileged -v "$REPO_DIR":/repo -v "${DOCKER_SCRATCH}":/scratch/docker -e REPO_ROOT=/repo -e KEEP_RUNNING="" -e PARALLELISM="${PARALLELISM}" haproxy-boshrelease-testflight bash -c "cd /repo/ci/scripts && ./acceptance-tests"
echo "Cleaning up docker scratch: ${DOCKER_SCRATCH}"
sudo rm -rf "${DOCKER_SCRATCH}"
fi
16 changes: 13 additions & 3 deletions ci/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
FROM bosh/docker-cpi:main
FROM ghcr.io/cloudfoundry/bosh/docker-cpi:latest

# Install all necessary tools for haproxy testflight and dependency autobump
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y wget jq git vim nano python3-pip && \
apt-get install -y wget jq git vim nano python3-pip python3-venv && \
apt-get clean

# Set bosh env at login
RUN echo "source /tmp/local-bosh/director/docker-env" >> /root/.bashrc
RUN echo "source /tmp/local-bosh/director/env" >> /root/.bashrc

RUN mkdir -p /usr/local/bosh-deployment/haproxy-boshrelease

# Copy ops files
COPY ops/bosh-scaled-out.yml /usr/local/bosh-deployment/haproxy-boshrelease/bosh-scaled-out.yml
COPY ops/bosh-timeouts.yml /usr/local/bosh-deployment/haproxy-boshrelease/bosh-timeouts.yml
COPY ops/compilation.yml /usr/local/bosh-deployment/haproxy-boshrelease/compilation.yml

# Install Python libraries needed for scripts
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:${PATH}"
COPY scripts/requirements.txt /requirements.txt
RUN /usr/bin/python3 -m pip install -r /requirements.txt
RUN pip install -r /requirements.txt

# Install go dependencies
ENV GOBIN=/usr/local/bin
Expand Down
3 changes: 3 additions & 0 deletions ci/ops/bosh-scaled-out.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- type: replace
path: /instance_groups/name=bosh/properties/director/workers?
value: 12
3 changes: 3 additions & 0 deletions ci/ops/bosh-timeouts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- type: replace
path: /instance_groups/name=bosh/properties/director/db/connection_wait_timeout?
value: 60
File renamed without changes.
Loading