Files
coolify/scripts/transfer-test/lib.sh
T

154 lines
5.5 KiB
Bash

#!/usr/bin/env bash
# Shared values and helpers of the server transfer test kit. The other scripts source this file.
#
# Source (A): the dev instance of this checkout, as `./scripts/dev` names it. Override: TRANSFER_TEST_SOURCE.
# Target (B): a side-by-side instance of the same checkout. Default "transfer-target". Override: TRANSFER_TEST_TARGET.
# Tokens and the VM SSH key are stored in <main checkout>/.dev-instances/transfer-test/<source>/ (not in git).
set -uo pipefail
TT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
ROOT="$(cd "$TT_DIR/../.." && pwd -P)"
cd "$ROOT"
ENV_DIR="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")/.dev-instances"
SOURCE="${TRANSFER_TEST_SOURCE:-$(./scripts/dev container | sed 's/^coolify-dev-//')}"
TARGET="${TRANSFER_TEST_TARGET:-transfer-target}"
A="coolify-dev-$SOURCE"
B="coolify-dev-$TARGET"
STATE_DIR="$ENV_DIR/transfer-test/$SOURCE"
VM_KEY="$STATE_DIR/vm_key"
# The VM servers that the tests transfer, and the profile that is localhost of the source.
TRANSFER_PROFILES=(ubuntu-non-root debian-root)
LOCALHOST_PROFILE=ubuntu-root
PRIVILEGED=()
if [[ $EUID -ne 0 ]]; then
PRIVILEGED=(sudo -n -E)
fi
die() {
echo "ERROR: $*" >&2
exit 1
}
env_value() {
local file="$ENV_DIR/$1.env"
[[ -f "$file" ]] || die "No dev instance '$1' ($file). See ./scripts/dev urls"
grep -E "^$2=" "$file" | cut -d= -f2-
}
A_PORT="$(env_value "$SOURCE" APP_PORT)"
A_SLOT="$(env_value "$SOURCE" DEVELOPMENT_QEMU_SLOT)"
A_URL="$(env_value "$SOURCE" APP_URL)"
target_values() {
B_PORT="$(env_value "$TARGET" APP_PORT)"
B_URL="$(env_value "$TARGET" APP_URL)"
}
if [[ -f "$ENV_DIR/$TARGET.env" ]]; then
target_values
fi
# vm_field <profile> <field>: a value of config/development-qemu.php for the source instance (ip, user, uuid).
vm_field() {
DEVELOPMENT_QEMU_INSTANCE="$SOURCE" DEVELOPMENT_QEMU_SLOT="$A_SLOT" php -r '
function env($key, $default = null) { $value = getenv($key); return $value === false ? $default : $value; }
$config = require "config/development-qemu.php";
echo $config["profiles"][$argv[1]][$argv[2]];
' "$1" "$2"
}
running() { [[ "$(docker inspect --format '{{.State.Running}}' "$1" 2>/dev/null)" == true ]]; }
# tinker <container> <php>: run PHP in a Coolify container.
tinker() { docker exec -u www-data "$1" php artisan tinker --execute "$2" 2>/dev/null; }
# php_file <container> <file>: run a PHP file of this kit in a Coolify container.
php_file() {
docker cp "$2" "$1:/tmp/$(basename "$2")" >/dev/null &&
docker exec -u www-data "$1" php artisan tinker --execute "require '/tmp/$(basename "$2")';" 2>&1 | grep -v '^$'
}
# api <METHOD> <port> <path> <token> [json]
api() {
curl -sS -m 30 -X "$1" "http://127.0.0.1:$2/api/v1$3" -H "Authorization: Bearer $4" \
-H 'Content-Type: application/json' -H 'Accept: application/json' ${5:+-d "$5"}
}
source_token() { cat "$STATE_DIR/source.token"; }
target_token() { cat "$STATE_DIR/target.token"; }
# The VMs accept the dev key that the seeders create as private key 1.
fetch_vm_key() {
mkdir -p "$STATE_DIR"
tinker "$A" 'echo optional(App\Models\PrivateKey::find(1))->private_key;' >"$VM_KEY.tmp"
if grep -q 'PRIVATE KEY' "$VM_KEY.tmp"; then
mv "$VM_KEY.tmp" "$VM_KEY"
chmod 600 "$VM_KEY"
return 0
fi
rm -f "$VM_KEY.tmp"
return 1
}
# ssh_vm <profile> <command>: run a shell command as root on a VM of the source.
ssh_vm() {
local user ip sudo=''
user="$(vm_field "$1" user)"
ip="$(vm_field "$1" ip)"
[[ "$user" != root ]] && sudo='sudo '
ssh -i "$VM_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5 \
"$user@$ip" "${sudo}sh -c $(printf '%q' "$2")"
}
# workloads <profile>: the containers on a VM, without Sentinel, which a claim restarts on purpose.
workloads() { ssh_vm "$1" 'docker ps --format "{{.Names}} {{.ID}}" | grep -v coolify-sentinel | sort'; }
# stable_workloads <profile>: the workloads once they stop changing. reset.sh returns while databases still start.
stable_workloads() {
local previous='' current
for _ in $(seq 1 30); do
current="$(workloads "$1")"
[[ -n "$current" && "$current" == "$previous" ]] && break
previous="$current"
sleep 10
done
echo "$current"
}
# require_dev_instance <container>: refuse to wipe anything that is not a running local dev instance.
require_dev_instance() {
running "$1" || die "Container $1 is not running."
[[ "$1" == coolify-dev-* ]] || die "$1 is not a scripts/dev instance."
[[ "$(tinker "$1" 'echo app()->environment();' | tail -1)" == local ]] || die "$1 does not run with APP_ENV=local."
}
# Pass/fail counter of the e2e scripts.
FAILS=0
ok() { echo " PASS $*"; }
bad() {
echo " FAIL $*"
FAILS=$((FAILS + 1))
}
check() {
local name="$1"
shift
if "$@" >/dev/null 2>&1; then ok "$name"; else bad "$name"; fi
}
login_php="auth()->login(App\\Models\\User::find(0)); session(['currentTeam' => App\\Models\\Team::find(0)]);"
# transfer_from_page <server uuid>: start a transfer A -> B through the Transfer page, wait, print the final status.
transfer_from_page() {
local status
tinker "$A" "$login_php Livewire\\Livewire::test(App\\Livewire\\Server\\Transfer::class, ['server_uuid' => '$1'])->set('targetUrl', 'http://localhost:$B_PORT')->set('targetToken', '$(target_token)')->call('migrateServer');" >/dev/null
for _ in $(seq 1 60); do
sleep 3
status="$(tinker "$A" "echo data_get(App\\Models\\Server::where('uuid', '$1')->first()?->server_metadata, 'migration.status');" | tail -1)"
[[ "$status" != running ]] && break
done
echo "$status"
}