Add isolated kind dependency testbed
This commit is contained in:
parent
74ac822d43
commit
5759938f3f
13 changed files with 1155 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
/docs/protocol/states/
|
||||
/states/
|
||||
/tests/kind/.runtime/
|
||||
docs/journal
|
||||
scratchpad
|
||||
|
|
|
|||
7
Makefile
Normal file
7
Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.PHONY: check-dependencies check-fixture-lib
|
||||
|
||||
check-dependencies:
|
||||
./tests/kind/run.sh
|
||||
|
||||
check-fixture-lib:
|
||||
./tests/kind/lib-test.sh
|
||||
26
docs/spec/testing.md
Normal file
26
docs/spec/testing.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Dependency testing
|
||||
|
||||
Backend integration tests execute inside an isolated kind cluster so they use
|
||||
the same DNS and service routing boundaries as deployed Nuthatch components.
|
||||
The fixture provides PostgreSQL, Redis and DragonflyDB, Keycloak and ZITADEL,
|
||||
and Garage and VersityGW. Images and fixture tools are checksum or
|
||||
digest-pinned.
|
||||
|
||||
`make check-dependencies` creates the complete fixture, verifies each released
|
||||
dependency image through its product interface, and destroys the fixture.
|
||||
`tests/kind/setup.sh` creates the same ready fixture for interactive test work;
|
||||
`tests/kind/destroy.sh` removes it. `NUTHATCH_TESTBED_RETAIN=1` retains a failed
|
||||
all-in-one run for inspection.
|
||||
|
||||
The fixture uses `tests/kind/.runtime/kubeconfig` and the
|
||||
`kind-nuthatch-testbed` context. Every Kubernetes operation supplies both
|
||||
explicitly. Fixture lifecycle operations are serialized by a host-runtime lock
|
||||
shared across worktrees, and teardown is idempotent.
|
||||
|
||||
All fixture credentials and data are synthetic. Test workloads use ClusterIP
|
||||
service names and do not require host port forwarding. The fixture does not
|
||||
read or change the user's Kubernetes context.
|
||||
|
||||
The host provides rootless nerdctl and containerd at the versions recorded in
|
||||
`tests/kind/versions.env`, together with `ctr`, `curl`, `flock`, `jq`, and
|
||||
`sha256sum`. The fixture downloads its pinned kind and kubectl binaries.
|
||||
7
tests/kind/cluster.yaml
Normal file
7
tests/kind/cluster.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
networking:
|
||||
apiServerAddress: 127.0.0.1
|
||||
apiServerPort: 36443
|
||||
nodes:
|
||||
- role: control-plane
|
||||
463
tests/kind/dependencies.yaml.in
Normal file
463
tests/kind/dependencies.yaml.in
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: nuthatch-testbed
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: postgres-init
|
||||
namespace: nuthatch-testbed
|
||||
data:
|
||||
01-keycloak.sql: |
|
||||
CREATE USER keycloak WITH PASSWORD 'synthetic-keycloak-db';
|
||||
CREATE DATABASE keycloak OWNER keycloak;
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: postgres}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: postgres}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: postgres
|
||||
image: @@POSTGRES_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- {name: POSTGRES_DB, value: nuthatch}
|
||||
- {name: POSTGRES_USER, value: nuthatch}
|
||||
- {name: POSTGRES_PASSWORD, value: synthetic-postgres}
|
||||
ports:
|
||||
- {name: postgres, containerPort: 5432}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [pg_isready, -U, nuthatch, -d, nuthatch]
|
||||
periodSeconds: 2
|
||||
resources:
|
||||
requests: {cpu: 100m, memory: 256Mi}
|
||||
limits: {cpu: "2", memory: 1Gi}
|
||||
volumeMounts:
|
||||
- {name: data, mountPath: /var/lib/postgresql}
|
||||
- {name: init, mountPath: /docker-entrypoint-initdb.d}
|
||||
volumes:
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
- name: init
|
||||
configMap: {name: postgres-init}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
selector: {app: postgres}
|
||||
ports:
|
||||
- {name: postgres, port: 5432, targetPort: postgres}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: redis}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: redis}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: redis
|
||||
image: @@REDIS_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [redis-server, --save, "", --appendonly, "no"]
|
||||
ports:
|
||||
- {name: redis, containerPort: 6379}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [redis-cli, ping]
|
||||
periodSeconds: 2
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 256Mi}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
selector: {app: redis}
|
||||
ports:
|
||||
- {name: redis, port: 6379, targetPort: redis}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dragonfly
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: dragonfly}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: dragonfly}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: dragonfly
|
||||
image: @@DRAGONFLY_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [--proactor_threads=1, --maxmemory=256mb]
|
||||
ports:
|
||||
- {name: redis, containerPort: 6379}
|
||||
readinessProbe:
|
||||
tcpSocket: {port: redis}
|
||||
periodSeconds: 2
|
||||
resources:
|
||||
requests: {cpu: 100m, memory: 64Mi}
|
||||
limits: {cpu: "2", memory: 384Mi}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: dragonfly
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
selector: {app: dragonfly}
|
||||
ports:
|
||||
- {name: redis, port: 6379, targetPort: redis}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: keycloak}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: keycloak}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: keycloak
|
||||
image: @@KEYCLOAK_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [start-dev, --hostname=http://keycloak.nuthatch.test:8080]
|
||||
env:
|
||||
- {name: KC_BOOTSTRAP_ADMIN_USERNAME, value: nuthatch-admin}
|
||||
- {name: KC_BOOTSTRAP_ADMIN_PASSWORD, value: synthetic-keycloak}
|
||||
- {name: KC_DB, value: postgres}
|
||||
- {name: KC_DB_URL, value: jdbc:postgresql://postgres:5432/keycloak}
|
||||
- {name: KC_DB_USERNAME, value: keycloak}
|
||||
- {name: KC_DB_PASSWORD, value: synthetic-keycloak-db}
|
||||
- {name: JAVA_OPTS_KC_HEAP, value: "-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=30 -XX:MaxRAMPercentage=65"}
|
||||
ports:
|
||||
- {name: http, containerPort: 8080}
|
||||
readinessProbe:
|
||||
httpGet: {path: /realms/master, port: http}
|
||||
periodSeconds: 3
|
||||
failureThreshold: 60
|
||||
resources:
|
||||
requests: {cpu: 200m, memory: 512Mi}
|
||||
limits: {cpu: "2", memory: 1Gi}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
clusterIP: 10.96.0.55
|
||||
selector: {app: keycloak}
|
||||
ports:
|
||||
- {name: http, port: 8080, targetPort: http}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zitadel
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: zitadel}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: zitadel}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: zitadel
|
||||
image: @@ZITADEL_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [start-from-init, --masterkeyFromEnv, --tlsMode, disabled]
|
||||
env:
|
||||
- {name: ZITADEL_MASTERKEY, value: synthetic-masterkey-32-character}
|
||||
- {name: ZITADEL_DATABASE_POSTGRES_DSN, value: "postgresql://nuthatch:synthetic-postgres@postgres:5432/postgres?sslmode=disable"}
|
||||
- {name: ZITADEL_EXTERNALSECURE, value: "false"}
|
||||
- {name: ZITADEL_EXTERNALDOMAIN, value: zitadel.nuthatch.test}
|
||||
- {name: ZITADEL_EXTERNALPORT, value: "8080"}
|
||||
- {name: ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME, value: nuthatch-admin}
|
||||
- {name: ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD, value: Synthetic-Zitadel-1!}
|
||||
ports:
|
||||
- {name: http, containerPort: 8080}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /.well-known/openid-configuration
|
||||
port: http
|
||||
httpHeaders:
|
||||
- {name: Host, value: zitadel.nuthatch.test}
|
||||
periodSeconds: 3
|
||||
failureThreshold: 80
|
||||
resources:
|
||||
requests: {cpu: 200m, memory: 512Mi}
|
||||
limits: {cpu: "2", memory: 1Gi}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: zitadel
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
clusterIP: 10.96.0.56
|
||||
selector: {app: zitadel}
|
||||
ports:
|
||||
- {name: http, port: 8080, targetPort: http}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: garage-config
|
||||
namespace: nuthatch-testbed
|
||||
data:
|
||||
garage.toml: |
|
||||
metadata_dir = "/data/meta"
|
||||
data_dir = "/data/data"
|
||||
db_engine = "sqlite"
|
||||
replication_factor = 1
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "garage:3901"
|
||||
rpc_secret = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
root_domain = ".s3.garage.nuthatch.test"
|
||||
[admin]
|
||||
api_bind_addr = "[::]:3903"
|
||||
admin_token = "synthetic-garage-admin"
|
||||
metrics_token = "synthetic-garage-metrics"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: garage
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: garage}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: garage}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: garage
|
||||
image: @@GARAGE_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/garage]
|
||||
args: [server, --single-node, --default-bucket]
|
||||
env:
|
||||
- {name: GARAGE_DEFAULT_ACCESS_KEY, value: GK00000000000000000000000000000000}
|
||||
- {name: GARAGE_DEFAULT_SECRET_KEY, value: "0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
- {name: GARAGE_DEFAULT_BUCKET, value: nuthatch-fixture}
|
||||
ports:
|
||||
- {name: s3, containerPort: 3900}
|
||||
resources:
|
||||
requests: {cpu: 100m, memory: 128Mi}
|
||||
limits: {cpu: "2", memory: 512Mi}
|
||||
volumeMounts:
|
||||
- {name: config, mountPath: /etc/garage.toml, subPath: garage.toml}
|
||||
- {name: data, mountPath: /data}
|
||||
- name: s3-readiness
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["while :; do sleep 3600; done"]
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: GK00000000000000000000000000000000}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: "0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
- {name: AWS_DEFAULT_REGION, value: garage}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [aws, --endpoint-url, "http://127.0.0.1:3900", s3api, head-bucket, --bucket, nuthatch-fixture]
|
||||
periodSeconds: 2
|
||||
failureThreshold: 60
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 64Mi}
|
||||
limits: {cpu: "1", memory: 256Mi}
|
||||
volumes:
|
||||
- name: config
|
||||
configMap: {name: garage-config}
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: garage
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
selector: {app: garage}
|
||||
ports:
|
||||
- {name: s3, port: 3900, targetPort: s3}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: versitygw
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: versitygw}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: versitygw}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: versitygw
|
||||
image: @@VERSITYGW_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [--port, ":10000", --iam-dir, /data, posix, --versioning-dir, /versions, /data]
|
||||
env:
|
||||
- {name: ROOT_ACCESS_KEY, value: synthetic-s3-access}
|
||||
- {name: ROOT_SECRET_KEY, value: synthetic-s3-secret}
|
||||
ports:
|
||||
- {name: s3, containerPort: 10000}
|
||||
resources:
|
||||
requests: {cpu: 100m, memory: 64Mi}
|
||||
limits: {cpu: "2", memory: 512Mi}
|
||||
volumeMounts:
|
||||
- {name: data, mountPath: /data}
|
||||
- {name: versions, mountPath: /versions}
|
||||
- name: s3-readiness
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["while :; do sleep 3600; done"]
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: synthetic-s3-access}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: synthetic-s3-secret}
|
||||
- {name: AWS_DEFAULT_REGION, value: us-east-1}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [aws, --endpoint-url, "http://127.0.0.1:10000", s3api, list-buckets]
|
||||
periodSeconds: 2
|
||||
failureThreshold: 60
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 64Mi}
|
||||
limits: {cpu: "1", memory: 256Mi}
|
||||
volumes:
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
- name: versions
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: versitygw
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
selector: {app: versitygw}
|
||||
ports:
|
||||
- {name: s3, port: 10000, targetPort: s3}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: controlled-dns
|
||||
namespace: nuthatch-testbed
|
||||
data:
|
||||
Corefile: |
|
||||
.:53 {
|
||||
errors
|
||||
hosts {
|
||||
10.96.0.55 keycloak.nuthatch.test
|
||||
10.96.0.56 zitadel.nuthatch.test
|
||||
fallthrough
|
||||
}
|
||||
forward . 10.96.0.10
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controlled-dns
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: {app: controlled-dns}
|
||||
template:
|
||||
metadata:
|
||||
labels: {app: controlled-dns}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
containers:
|
||||
- name: coredns
|
||||
image: @@COREDNS_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
args: [-conf, /etc/coredns/Corefile]
|
||||
ports:
|
||||
- {name: dns-udp, containerPort: 53, protocol: UDP}
|
||||
- {name: dns-tcp, containerPort: 53, protocol: TCP}
|
||||
readinessProbe:
|
||||
tcpSocket: {port: dns-tcp}
|
||||
periodSeconds: 2
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 128Mi}
|
||||
volumeMounts:
|
||||
- {name: config, mountPath: /etc/coredns}
|
||||
volumes:
|
||||
- name: config
|
||||
configMap: {name: controlled-dns}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: controlled-dns
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
clusterIP: 10.96.0.53
|
||||
selector: {app: controlled-dns}
|
||||
ports:
|
||||
- {name: dns-udp, port: 53, targetPort: dns-udp, protocol: UDP}
|
||||
- {name: dns-tcp, port: 53, targetPort: dns-tcp, protocol: TCP}
|
||||
9
tests/kind/destroy.sh
Executable file
9
tests/kind/destroy.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
source "$ROOT/tests/kind/lib.sh"
|
||||
|
||||
acquire_fixture_lock
|
||||
prepare_tools
|
||||
destroy_fixture
|
||||
22
tests/kind/lib-test.sh
Executable file
22
tests/kind/lib-test.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
source "$ROOT/tests/kind/lib.sh"
|
||||
|
||||
if kube get namespaces >/dev/null 2>&1; then
|
||||
printf 'kube helper unexpectedly reached a cluster without fixture setup\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FIXTURE_LOCK_DIR="$RUNTIME/lock-test"
|
||||
FIXTURE_LOCK_PATH="$FIXTURE_LOCK_DIR/nuthatch-testbed-kind.lock"
|
||||
acquire_fixture_lock
|
||||
if XDG_RUNTIME_DIR="$FIXTURE_LOCK_DIR" bash -c \
|
||||
'source "$1"; acquire_fixture_lock' _ "$ROOT/tests/kind/lib.sh" 9>&-; then
|
||||
printf 'singleton fixture lock admitted a second worktree process\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
exec 9>&-
|
||||
|
||||
printf 'fixture isolation tests passed\n'
|
||||
269
tests/kind/lib.sh
Executable file
269
tests/kind/lib.sh
Executable file
|
|
@ -0,0 +1,269 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
TESTBED="$ROOT/tests/kind"
|
||||
RUNTIME="$TESTBED/.runtime"
|
||||
BIN="$RUNTIME/bin"
|
||||
KUBECONFIG_PATH="$RUNTIME/kubeconfig"
|
||||
FIXTURE_LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp/nuthatch-$(id -u)}"
|
||||
FIXTURE_LOCK_PATH="$FIXTURE_LOCK_DIR/nuthatch-testbed-kind.lock"
|
||||
CONTEXT=kind-nuthatch-testbed
|
||||
CLUSTER=nuthatch-testbed
|
||||
NERDCTL_NAMESPACE=nuthatch-testbed
|
||||
FIXTURE_NAMESPACE=nuthatch-testbed
|
||||
|
||||
set -a
|
||||
source "$TESTBED/versions.env"
|
||||
set +a
|
||||
|
||||
user_kubeconfig_digest() {
|
||||
local config_value="${KUBECONFIG:-${HOME:?}/.kube/config}"
|
||||
local path
|
||||
local paths
|
||||
IFS=: read -r -a paths <<< "$config_value"
|
||||
{
|
||||
for path in "${paths[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
sha256sum "$path"
|
||||
else
|
||||
printf 'missing %s\n' "$path"
|
||||
fi
|
||||
done
|
||||
} | sha256sum | cut -d' ' -f1
|
||||
}
|
||||
|
||||
download_tool() {
|
||||
local destination="$1"
|
||||
local checksum="$2"
|
||||
local url="$3"
|
||||
if [[ -x "$destination" ]] &&
|
||||
[[ "$(sha256sum "$destination" | cut -d' ' -f1)" = "$checksum" ]]; then
|
||||
return
|
||||
fi
|
||||
curl --fail --location --silent --show-error "$url" --output "$destination.tmp"
|
||||
test "$(sha256sum "$destination.tmp" | cut -d' ' -f1)" = "$checksum"
|
||||
chmod 0755 "$destination.tmp"
|
||||
mv "$destination.tmp" "$destination"
|
||||
}
|
||||
|
||||
prepare_tools() {
|
||||
mkdir -p "$BIN"
|
||||
download_tool \
|
||||
"$BIN/kind" \
|
||||
"$KIND_SHA256" \
|
||||
"https://github.com/kubernetes-sigs/kind/releases/download/$KIND_VERSION/kind-linux-amd64"
|
||||
download_tool \
|
||||
"$BIN/kubectl" \
|
||||
"$KUBECTL_SHA256" \
|
||||
"https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl"
|
||||
test "$("$BIN/kind" version | awk '{print $2}')" = "$KIND_VERSION"
|
||||
test "$("$BIN/kubectl" --kubeconfig "$KUBECONFIG_PATH" --context "$CONTEXT" version --client=true -o json | jq -r .clientVersion.gitVersion)" = "$KUBECTL_VERSION"
|
||||
test "$(nerdctl --version | awk '{print $3}')" = "$NERDCTL_VERSION"
|
||||
test "$(runtime_ctr version | awk '/Client:/{client=1} client && $1=="Version:"{print $2; exit}')" = "v$CONTAINERD_VERSION"
|
||||
test "$(runtime_ctr version | awk '/Server:/{server=1} server && $1=="Version:"{print $2; exit}')" = "v$CONTAINERD_VERSION"
|
||||
}
|
||||
|
||||
acquire_fixture_lock() {
|
||||
mkdir -p "$RUNTIME" "$FIXTURE_LOCK_DIR"
|
||||
exec 9>"$FIXTURE_LOCK_PATH"
|
||||
if ! flock --nonblock 9; then
|
||||
printf 'kind test fixture is already in use\n' >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
configure_provider() {
|
||||
if [[ -z "${NUTHATCH_REAL_NERDCTL:-}" ]]; then
|
||||
NUTHATCH_REAL_NERDCTL="$(command -v nerdctl)"
|
||||
fi
|
||||
export NUTHATCH_REAL_NERDCTL
|
||||
export CONTAINERD_NAMESPACE="$NERDCTL_NAMESPACE"
|
||||
export KIND_EXPERIMENTAL_PROVIDER=nerdctl
|
||||
export PATH="$TESTBED/provider:$PATH"
|
||||
}
|
||||
|
||||
runtime_ctr() {
|
||||
ctr --address "${CONTAINERD_ADDRESS:-${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/containerd/containerd.sock}" "$@"
|
||||
}
|
||||
|
||||
runtime_namespace_exists() {
|
||||
"$NUTHATCH_REAL_NERDCTL" namespace list --quiet | grep -Fx "$NERDCTL_NAMESPACE" >/dev/null
|
||||
}
|
||||
|
||||
cluster_exists() {
|
||||
runtime_namespace_exists &&
|
||||
"$BIN/kind" get clusters | grep -Fx "$CLUSTER" >/dev/null
|
||||
}
|
||||
|
||||
delete_cluster() {
|
||||
local container
|
||||
local containers
|
||||
if cluster_exists; then
|
||||
"$BIN/kind" delete cluster --name "$CLUSTER" || true
|
||||
fi
|
||||
containers="$("$NUTHATCH_REAL_NERDCTL" --namespace "$NERDCTL_NAMESPACE" ps \
|
||||
--all \
|
||||
--quiet \
|
||||
--filter "label=io.x-k8s.kind.cluster=$CLUSTER")"
|
||||
while IFS= read -r container; do
|
||||
[[ -z "$container" ]] ||
|
||||
"$NUTHATCH_REAL_NERDCTL" --namespace "$NERDCTL_NAMESPACE" rm --force "$container"
|
||||
done <<< "$containers"
|
||||
if cluster_exists; then
|
||||
"$BIN/kind" delete cluster --name "$CLUSTER" || true
|
||||
fi
|
||||
if cluster_exists; then
|
||||
printf 'kind cluster %s still exists after teardown\n' "$CLUSTER" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
remove_runtime_namespace() {
|
||||
local status=0
|
||||
local digest
|
||||
local digests
|
||||
local lease
|
||||
local leases
|
||||
local network
|
||||
for network in kind nuthatch-kind; do
|
||||
if "$NUTHATCH_REAL_NERDCTL" --namespace "$NERDCTL_NAMESPACE" network inspect "$network" >/dev/null 2>&1; then
|
||||
"$NUTHATCH_REAL_NERDCTL" --namespace "$NERDCTL_NAMESPACE" network remove "$network" >/dev/null || status=1
|
||||
fi
|
||||
done
|
||||
if ! runtime_namespace_exists; then
|
||||
return "$status"
|
||||
fi
|
||||
"$NUTHATCH_REAL_NERDCTL" --namespace "$NERDCTL_NAMESPACE" image prune --all --force >/dev/null || status=1
|
||||
leases="$(runtime_ctr --namespace "$NERDCTL_NAMESPACE" leases list --quiet)" || status=1
|
||||
while IFS= read -r lease; do
|
||||
[[ -z "$lease" ]] || runtime_ctr --namespace "$NERDCTL_NAMESPACE" leases delete "$lease" >/dev/null || status=1
|
||||
done <<< "$leases"
|
||||
digests="$(runtime_ctr --namespace "$NERDCTL_NAMESPACE" content list --quiet)" || status=1
|
||||
while IFS= read -r digest; do
|
||||
[[ -z "$digest" ]] || runtime_ctr --namespace "$NERDCTL_NAMESPACE" content remove "$digest" >/dev/null || status=1
|
||||
done <<< "$digests"
|
||||
"$NUTHATCH_REAL_NERDCTL" namespace remove "$NERDCTL_NAMESPACE" >/dev/null || status=1
|
||||
runtime_namespace_exists && status=1
|
||||
return "$status"
|
||||
}
|
||||
|
||||
prepare_provider() {
|
||||
configure_provider
|
||||
delete_cluster
|
||||
remove_runtime_namespace
|
||||
"$NUTHATCH_REAL_NERDCTL" namespace create "$NERDCTL_NAMESPACE" >/dev/null
|
||||
}
|
||||
|
||||
load_external_image() {
|
||||
local name="$1"
|
||||
local source="$2"
|
||||
local digest="${source##*@}"
|
||||
local image="nuthatch-testbed/$name:sha256-${digest#sha256:}"
|
||||
local archive="$RUNTIME/image-archives/$digest.tar"
|
||||
"$NUTHATCH_REAL_NERDCTL" image pull --platform linux/amd64 --unpack=false --quiet "$source" >&2
|
||||
"$NUTHATCH_REAL_NERDCTL" image tag "$source" "$image"
|
||||
"$NUTHATCH_REAL_NERDCTL" image save --platform linux/amd64 --output "$archive" "$image"
|
||||
printf 'Loading %s from %s\n' "$image" "$source" >&2
|
||||
"$BIN/kind" load image-archive "$archive" --name "$CLUSTER" >&2
|
||||
printf '%s\n' "$image"
|
||||
}
|
||||
|
||||
render_manifests() {
|
||||
local postgres="$1" redis="$2" dragonfly="$3" keycloak="$4" zitadel="$5"
|
||||
local garage="$6" versitygw="$7" coredns="$8" curl="$9" aws_cli="${10}"
|
||||
sed \
|
||||
-e "s|@@POSTGRES_IMAGE@@|$postgres|g" \
|
||||
-e "s|@@REDIS_IMAGE@@|$redis|g" \
|
||||
-e "s|@@DRAGONFLY_IMAGE@@|$dragonfly|g" \
|
||||
-e "s|@@KEYCLOAK_IMAGE@@|$keycloak|g" \
|
||||
-e "s|@@ZITADEL_IMAGE@@|$zitadel|g" \
|
||||
-e "s|@@GARAGE_IMAGE@@|$garage|g" \
|
||||
-e "s|@@VERSITYGW_IMAGE@@|$versitygw|g" \
|
||||
-e "s|@@COREDNS_IMAGE@@|$coredns|g" \
|
||||
-e "s|@@AWS_CLI_IMAGE@@|$aws_cli|g" \
|
||||
"$TESTBED/dependencies.yaml.in" > "$RUNTIME/dependencies.yaml"
|
||||
sed \
|
||||
-e "s|@@POSTGRES_IMAGE@@|$postgres|g" \
|
||||
-e "s|@@REDIS_IMAGE@@|$redis|g" \
|
||||
-e "s|@@CURL_IMAGE@@|$curl|g" \
|
||||
-e "s|@@AWS_CLI_IMAGE@@|$aws_cli|g" \
|
||||
"$TESTBED/probe.yaml.in" > "$RUNTIME/probe.yaml"
|
||||
}
|
||||
|
||||
kube() {
|
||||
"$BIN/kubectl" \
|
||||
--kubeconfig "$KUBECONFIG_PATH" \
|
||||
--context "$CONTEXT" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
wait_for_job() {
|
||||
local job="$1"
|
||||
local deadline=$((SECONDS + 300))
|
||||
local complete
|
||||
local failed
|
||||
while ((SECONDS < deadline)); do
|
||||
complete="$(kube --namespace "$FIXTURE_NAMESPACE" get "job/$job" --output jsonpath='{.status.conditions[?(@.type=="Complete")].status}')"
|
||||
if [[ "$complete" = True ]]; then
|
||||
kube --namespace "$FIXTURE_NAMESPACE" logs "job/$job" --all-containers
|
||||
return
|
||||
fi
|
||||
failed="$(kube --namespace "$FIXTURE_NAMESPACE" get "job/$job" --output jsonpath='{.status.conditions[?(@.type=="Failed")].status}')"
|
||||
if [[ "$failed" = True ]]; then
|
||||
kube --namespace "$FIXTURE_NAMESPACE" logs "job/$job" --all-containers >&2 || true
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
printf 'job/%s did not complete within 300 seconds\n' "$job" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
setup_fixture() {
|
||||
mkdir -p "$RUNTIME"
|
||||
rm -rf "$RUNTIME/image-archives"
|
||||
mkdir -p "$RUNTIME/image-archives"
|
||||
prepare_tools
|
||||
prepare_provider
|
||||
"$BIN/kind" create cluster \
|
||||
--name "$CLUSTER" \
|
||||
--image "$NODE_IMAGE" \
|
||||
--config "$TESTBED/cluster.yaml" \
|
||||
--kubeconfig "$KUBECONFIG_PATH" \
|
||||
--wait 180s
|
||||
local postgres redis dragonfly keycloak zitadel garage versitygw coredns curl aws_cli
|
||||
postgres="$(load_external_image postgres "$POSTGRES_IMAGE")"
|
||||
redis="$(load_external_image redis "$REDIS_IMAGE")"
|
||||
dragonfly="$(load_external_image dragonfly "$DRAGONFLY_IMAGE")"
|
||||
keycloak="$(load_external_image keycloak "$KEYCLOAK_IMAGE")"
|
||||
zitadel="$(load_external_image zitadel "$ZITADEL_IMAGE")"
|
||||
garage="$(load_external_image garage "$GARAGE_IMAGE")"
|
||||
versitygw="$(load_external_image versitygw "$VERSITYGW_IMAGE")"
|
||||
coredns="$(load_external_image coredns "$COREDNS_IMAGE")"
|
||||
curl="$(load_external_image curl "$CURL_IMAGE")"
|
||||
aws_cli="$(load_external_image aws-cli "$AWS_CLI_IMAGE")"
|
||||
render_manifests "$postgres" "$redis" "$dragonfly" "$keycloak" "$zitadel" \
|
||||
"$garage" "$versitygw" "$coredns" "$curl" "$aws_cli"
|
||||
kube apply --filename "$RUNTIME/dependencies.yaml"
|
||||
local deployment
|
||||
for deployment in postgres redis dragonfly keycloak zitadel garage versitygw controlled-dns; do
|
||||
kube --namespace "$FIXTURE_NAMESPACE" rollout status "deployment/$deployment" --timeout 240s
|
||||
done
|
||||
}
|
||||
|
||||
verify_dependencies() {
|
||||
kube apply --filename "$RUNTIME/probe.yaml"
|
||||
local job
|
||||
for job in state-probe oidc-probe garage-probe versitygw-probe; do
|
||||
wait_for_job "$job"
|
||||
done
|
||||
}
|
||||
|
||||
destroy_fixture() {
|
||||
configure_provider
|
||||
delete_cluster
|
||||
remove_runtime_namespace
|
||||
rm -rf "$RUNTIME/image-archives"
|
||||
rm -f "$KUBECONFIG_PATH" "$RUNTIME/dependencies.yaml" "$RUNTIME/probe.yaml"
|
||||
}
|
||||
226
tests/kind/probe.yaml.in
Normal file
226
tests/kind/probe.yaml.in
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: probe-payload
|
||||
namespace: nuthatch-testbed
|
||||
data:
|
||||
payload: nuthatch-dependency-compatibility-probe
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: state-probe
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
restartPolicy: Never
|
||||
initContainers:
|
||||
- name: postgres
|
||||
image: @@POSTGRES_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [psql]
|
||||
args:
|
||||
- -h
|
||||
- postgres
|
||||
- -U
|
||||
- nuthatch
|
||||
- -d
|
||||
- nuthatch
|
||||
- -v
|
||||
- ON_ERROR_STOP=1
|
||||
- -c
|
||||
- "CREATE TABLE dependency_probe (value text NOT NULL)"
|
||||
- -c
|
||||
- "INSERT INTO dependency_probe VALUES ('passed')"
|
||||
- -c
|
||||
- "SELECT value = 'passed' FROM dependency_probe"
|
||||
env:
|
||||
- {name: PGPASSWORD, value: synthetic-postgres}
|
||||
- name: redis
|
||||
image: @@REDIS_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["redis-cli -h redis SET nuthatch-probe passed && test \"$(redis-cli -h redis GET nuthatch-probe)\" = passed"]
|
||||
containers:
|
||||
- name: dragonfly
|
||||
image: @@REDIS_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["redis-cli -h dragonfly SET nuthatch-probe passed && test \"$(redis-cli -h dragonfly GET nuthatch-probe)\" = passed && printf 'PostgreSQL and Redis-compatible stores passed\\n'"]
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 128Mi}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: oidc-probe
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
restartPolicy: Never
|
||||
dnsPolicy: None
|
||||
dnsConfig:
|
||||
nameservers: [10.96.0.53]
|
||||
containers:
|
||||
- name: curl
|
||||
image: @@CURL_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args:
|
||||
- |
|
||||
probe() {
|
||||
url="$1"
|
||||
pattern="$2"
|
||||
attempt=0
|
||||
until curl --fail --silent --show-error "$url" | grep -q "$pattern"; do
|
||||
attempt=$((attempt + 1))
|
||||
test "$attempt" -lt 30
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
probe http://keycloak.nuthatch.test:8080/realms/master/.well-known/openid-configuration '"issuer"'
|
||||
probe http://keycloak.nuthatch.test:8080/realms/master/protocol/openid-connect/certs '"keys"'
|
||||
probe http://zitadel.nuthatch.test:8080/.well-known/openid-configuration '"issuer"'
|
||||
probe http://zitadel.nuthatch.test:8080/oauth/v2/keys '"keys"'
|
||||
printf 'Keycloak and ZITADEL discovery passed through controlled DNS\n'
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 128Mi}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: garage-probe
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
restartPolicy: Never
|
||||
initContainers:
|
||||
- name: upload
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args:
|
||||
- |
|
||||
attempt=0
|
||||
until aws --endpoint-url http://garage:3900 s3api put-object --bucket nuthatch-fixture --key probe/payload --body /fixture/payload; do
|
||||
attempt=$((attempt + 1))
|
||||
test "$attempt" -lt 30
|
||||
sleep 1
|
||||
done
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: GK00000000000000000000000000000000}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: "0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
- {name: AWS_DEFAULT_REGION, value: garage}
|
||||
volumeMounts:
|
||||
- {name: fixture, mountPath: /fixture}
|
||||
- name: download
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [aws]
|
||||
args: [--endpoint-url, "http://garage:3900", s3api, get-object, --bucket, nuthatch-fixture, --key, probe/payload, /work/result]
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: GK00000000000000000000000000000000}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: "0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
- {name: AWS_DEFAULT_REGION, value: garage}
|
||||
volumeMounts:
|
||||
- {name: work, mountPath: /work}
|
||||
containers:
|
||||
- name: compare
|
||||
image: @@CURL_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["cmp /fixture/payload /work/result && printf 'Garage put/get passed\\n'"]
|
||||
volumeMounts:
|
||||
- {name: fixture, mountPath: /fixture}
|
||||
- {name: work, mountPath: /work}
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 128Mi}
|
||||
volumes:
|
||||
- name: fixture
|
||||
configMap: {name: probe-payload}
|
||||
- name: work
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: versitygw-probe
|
||||
namespace: nuthatch-testbed
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
restartPolicy: Never
|
||||
initContainers:
|
||||
- name: bucket
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args:
|
||||
- |
|
||||
aws --endpoint-url http://versitygw:10000 s3api create-bucket --bucket nuthatch-fixture ||
|
||||
aws --endpoint-url http://versitygw:10000 s3api head-bucket --bucket nuthatch-fixture
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: synthetic-s3-access}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: synthetic-s3-secret}
|
||||
- {name: AWS_DEFAULT_REGION, value: us-east-1}
|
||||
- name: upload
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [aws]
|
||||
args: [--endpoint-url, "http://versitygw:10000", s3api, put-object, --bucket, nuthatch-fixture, --key, probe/payload, --body, /fixture/payload]
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: synthetic-s3-access}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: synthetic-s3-secret}
|
||||
- {name: AWS_DEFAULT_REGION, value: us-east-1}
|
||||
volumeMounts:
|
||||
- {name: fixture, mountPath: /fixture}
|
||||
- name: download
|
||||
image: @@AWS_CLI_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [aws]
|
||||
args: [--endpoint-url, "http://versitygw:10000", s3api, get-object, --bucket, nuthatch-fixture, --key, probe/payload, /work/result]
|
||||
env:
|
||||
- {name: AWS_ACCESS_KEY_ID, value: synthetic-s3-access}
|
||||
- {name: AWS_SECRET_ACCESS_KEY, value: synthetic-s3-secret}
|
||||
- {name: AWS_DEFAULT_REGION, value: us-east-1}
|
||||
volumeMounts:
|
||||
- {name: work, mountPath: /work}
|
||||
containers:
|
||||
- name: compare
|
||||
image: @@CURL_IMAGE@@
|
||||
imagePullPolicy: Never
|
||||
command: [/bin/sh, -ec]
|
||||
args: ["cmp /fixture/payload /work/result && printf 'VersityGW put/get passed\\n'"]
|
||||
volumeMounts:
|
||||
- {name: fixture, mountPath: /fixture}
|
||||
- {name: work, mountPath: /work}
|
||||
resources:
|
||||
requests: {cpu: 25m, memory: 32Mi}
|
||||
limits: {cpu: "1", memory: 128Mi}
|
||||
volumes:
|
||||
- name: fixture
|
||||
configMap: {name: probe-payload}
|
||||
- name: work
|
||||
emptyDir: {}
|
||||
39
tests/kind/provider/nerdctl
Executable file
39
tests/kind/provider/nerdctl
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "${1:-}" = run ]]; then
|
||||
shift
|
||||
arguments=()
|
||||
for argument in "$@"; do
|
||||
if [[ "$argument" = kind ]]; then
|
||||
arguments+=(nuthatch-kind)
|
||||
else
|
||||
arguments+=("$argument")
|
||||
fi
|
||||
done
|
||||
exec "$NUTHATCH_REAL_NERDCTL" run --memory 20g --memory-swap 28g --cpus 8 "${arguments[@]}"
|
||||
fi
|
||||
|
||||
# kind otherwise gives every rootless namespace the same bridge and IPv6 subnet.
|
||||
if [[ "${1:-}" = network ]]; then
|
||||
arguments=()
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
kind)
|
||||
arguments+=(nuthatch-kind)
|
||||
;;
|
||||
fc00:f853:ccd:e793::/64)
|
||||
arguments+=(fc00:f853:ccd:e794::/64)
|
||||
;;
|
||||
--subnet=fc00:f853:ccd:e793::/64)
|
||||
arguments+=(--subnet=fc00:f853:ccd:e794::/64)
|
||||
;;
|
||||
*)
|
||||
arguments+=("$argument")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
exec "$NUTHATCH_REAL_NERDCTL" "${arguments[@]}"
|
||||
fi
|
||||
|
||||
exec "$NUTHATCH_REAL_NERDCTL" "$@"
|
||||
38
tests/kind/run.sh
Executable file
38
tests/kind/run.sh
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
source "$ROOT/tests/kind/lib.sh"
|
||||
|
||||
USER_KUBECONFIG_BEFORE="$(user_kubeconfig_digest)"
|
||||
SUCCEEDED=false
|
||||
|
||||
cleanup() {
|
||||
local status=$?
|
||||
trap - EXIT INT TERM
|
||||
if [[ "${NUTHATCH_TESTBED_RETAIN:-0}" = 1 ]] && [[ "$SUCCEEDED" != true ]]; then
|
||||
printf 'retained kubeconfig: %s\n' "$KUBECONFIG_PATH"
|
||||
printf 'retained context: %s\n' "$CONTEXT"
|
||||
exit "$status"
|
||||
fi
|
||||
if [[ -n "${NUTHATCH_REAL_NERDCTL:-}" ]]; then
|
||||
destroy_fixture || status=1
|
||||
fi
|
||||
if [[ "$(user_kubeconfig_digest)" != "$USER_KUBECONFIG_BEFORE" ]]; then
|
||||
printf 'user kubeconfig changed unexpectedly\n' >&2
|
||||
status=1
|
||||
fi
|
||||
if [[ "$status" = 0 ]] && [[ "$SUCCEEDED" = true ]]; then
|
||||
printf 'Nuthatch dependency testbed passed\n'
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
acquire_fixture_lock
|
||||
setup_fixture
|
||||
verify_dependencies
|
||||
SUCCEEDED=true
|
||||
31
tests/kind/setup.sh
Executable file
31
tests/kind/setup.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
source "$ROOT/tests/kind/lib.sh"
|
||||
|
||||
USER_KUBECONFIG_BEFORE="$(user_kubeconfig_digest)"
|
||||
SUCCEEDED=false
|
||||
|
||||
cleanup() {
|
||||
local status=$?
|
||||
trap - EXIT INT TERM
|
||||
if [[ "$SUCCEEDED" != true ]] && [[ "${NUTHATCH_TESTBED_RETAIN:-0}" != 1 ]]; then
|
||||
destroy_fixture || status=1
|
||||
fi
|
||||
if [[ "$(user_kubeconfig_digest)" != "$USER_KUBECONFIG_BEFORE" ]]; then
|
||||
printf 'user kubeconfig changed unexpectedly\n' >&2
|
||||
status=1
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
acquire_fixture_lock
|
||||
setup_fixture
|
||||
SUCCEEDED=true
|
||||
printf 'fixture kubeconfig: %s\n' "$KUBECONFIG_PATH"
|
||||
printf 'fixture context: %s\n' "$CONTEXT"
|
||||
17
tests/kind/versions.env
Normal file
17
tests/kind/versions.env
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
KIND_VERSION=v0.32.0
|
||||
KIND_SHA256=50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54
|
||||
KUBECTL_VERSION=v1.36.2
|
||||
KUBECTL_SHA256=1e9045ec32bea85da43de85f0065358529ea7c7a152eca78154fba5b58c27d82
|
||||
CONTAINERD_VERSION=2.3.3
|
||||
NERDCTL_VERSION=2.3.4
|
||||
NODE_IMAGE=kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5
|
||||
POSTGRES_IMAGE=postgres@sha256:bb9940981eeb86356ec91bda430f8e0ba8729cc0b319615a2c2d7ac932fdf7bb
|
||||
REDIS_IMAGE=redis@sha256:cd5f3ac681c77791c6a8eaa62de876ad2be043ee5a428afb7c0095aa08246277
|
||||
DRAGONFLY_IMAGE=docker.dragonflydb.io/dragonflydb/dragonfly@sha256:665ee58607d035881384be45b20025490ecf87069418f61f715c19117ed5fb7d
|
||||
KEYCLOAK_IMAGE=quay.io/keycloak/keycloak@sha256:1362a9d9f13ab325231ea133610cc905e12805804abc7acbef552dd613720aa6
|
||||
ZITADEL_IMAGE=ghcr.io/zitadel/zitadel@sha256:d59554f17d20c06c9f6ed114bf9f2c3a9a63698003daea62d27bdae1e9667583
|
||||
GARAGE_IMAGE=dxflrs/garage@sha256:dac0c92add4f1a0b41035e94b41036a270ffbe88a37c7ac9c3f19e6dc5bdccf2
|
||||
VERSITYGW_IMAGE=versity/versitygw@sha256:4a2c613f6541410f87983c5134cd26a88dfc23f0ad555b7aa51d3d87fd2d347b
|
||||
COREDNS_IMAGE=coredns/coredns@sha256:1ba6f47265602e2e50a9c4669e3a955e4298a0d30dc82f39293d4bf1a851e0ff
|
||||
CURL_IMAGE=curlimages/curl@sha256:1ab04d023ece37e6ec991bf3306ad04e0ef0084e94a5c6b6563cfcb9563169db
|
||||
AWS_CLI_IMAGE=amazon/aws-cli@sha256:10c3a6dff0fdcb3fbc86cd744709548ad041ebb6bbe14fbedc1595a5539d8d27
|
||||
Loading…
Reference in a new issue