setup agent runtime requirements

This commit is contained in:
Harivansh Rathi 2026-04-01 04:36:36 +00:00
parent 5d97c33d7e
commit 016e0ee581
17 changed files with 424 additions and 28 deletions

43
scripts/agent-bootstrap Executable file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
env_file="$repo_root/.env.agent"
example_env_file="$repo_root/.env.agent.example"
if [[ ! -f "$env_file" ]]; then
cp "$example_env_file" "$env_file"
fi
# shellcheck disable=SC1091
source "$repo_root/scripts/lib/runtime-env.sh"
mkdir -p "$BETTERNAS_EXPORT_PATH"
if [[ ! -f "$BETTERNAS_EXPORT_PATH/README.txt" ]]; then
cat >"$BETTERNAS_EXPORT_PATH/README.txt" <<EOF
betterNAS export
clone=${BETTERNAS_CLONE_NAME}
mount_url=${BETTERNAS_EXAMPLE_MOUNT_URL}
EOF
fi
pnpm install --frozen-lockfile
go work sync
cat <<EOF
Agent bootstrap complete for ${BETTERNAS_CLONE_NAME}
Env file: ${env_file}
Control plane: http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}
Node agent: http://localhost:${BETTERNAS_NODE_AGENT_PORT}
Nextcloud: ${NEXTCLOUD_BASE_URL}
Export path: ${BETTERNAS_EXPORT_PATH}
Mount URL: ${BETTERNAS_EXAMPLE_MOUNT_URL}
Next:
pnpm verify
pnpm stack:up
pnpm stack:verify
EOF

11
scripts/agent-verify Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
./scripts/agent-bootstrap
pnpm verify
./scripts/dev-up
./scripts/integration/verify-stack

View file

@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck disable=SC1091
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../lib/runtime-env.sh"
"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/wait-stack"
"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/verify-webdav"
control_health="$(curl -fsS "http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/health")"
echo "$control_health" | jq -e '.service == "control-plane" and .status == "ok"' >/dev/null
register_response="$(
curl -fsS \
-X POST \
-H 'Content-Type: application/json' \
-d @- \
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/nodes/register" <<JSON
{"machineId":"${BETTERNAS_CLONE_NAME}-machine","displayName":"${BETTERNAS_CLONE_NAME} node","agentVersion":"${BETTERNAS_VERSION}","directAddress":"${BETTERNAS_NODE_DIRECT_ADDRESS}","relayAddress":null,"exports":[{"label":"integration","path":"${BETTERNAS_EXPORT_PATH}","protocols":["webdav"],"capacityBytes":null,"tags":["integration"]}]}
JSON
)"
echo "$register_response" | jq -e '.status == "online"' >/dev/null
mount_profile="$(
curl -fsS \
-X POST \
-H 'Content-Type: application/json' \
-d '{"userId":"integration-user","deviceId":"integration-device","exportId":"dev-export"}' \
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/mount-profiles/issue"
)"
echo "$mount_profile" | jq -e --arg expected "$BETTERNAS_EXAMPLE_MOUNT_URL" '.protocol == "webdav" and .mountUrl == $expected' >/dev/null
cloud_profile="$(
curl -fsS \
-X POST \
-H 'Content-Type: application/json' \
-d '{"userId":"integration-user","exportId":"dev-export","provider":"nextcloud"}' \
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/cloud-profiles/issue"
)"
echo "$cloud_profile" | jq -e --arg expected "$NEXTCLOUD_BASE_URL" '.provider == "nextcloud" and .baseUrl == $expected' >/dev/null
nextcloud_status="$(curl -fsS "${NEXTCLOUD_BASE_URL}/status.php")"
echo "$nextcloud_status" | jq -e '.installed == true' >/dev/null
nextcloud_app_status="$(
curl -fsS \
-u "${NEXTCLOUD_ADMIN_USER}:${NEXTCLOUD_ADMIN_PASSWORD}" \
-H 'OCS-APIRequest: true' \
"${NEXTCLOUD_BASE_URL}/ocs/v2.php/apps/betternascontrolplane/api/status"
)"
echo "$nextcloud_app_status" | jq -e '.ocs.meta.statuscode == 100' >/dev/null
echo "Stack verified for ${BETTERNAS_CLONE_NAME}."

View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck disable=SC1091
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../lib/runtime-env.sh"
headers="$(mktemp)"
trap 'rm -f "$headers"' EXIT
curl -fsS -D "$headers" -o /dev/null -X PROPFIND -H 'Depth: 0' "$BETTERNAS_EXAMPLE_MOUNT_URL"
if ! grep -Eq '^HTTP/[0-9.]+ 207' "$headers"; then
echo "WebDAV PROPFIND did not return 207 for $BETTERNAS_EXAMPLE_MOUNT_URL" >&2
cat "$headers" >&2
exit 1
fi
echo "WebDAV verified: $BETTERNAS_EXAMPLE_MOUNT_URL"

27
scripts/integration/wait-stack Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck disable=SC1091
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../lib/runtime-env.sh"
wait_for_url() {
local url="$1"
local label="$2"
local max_attempts="${3:-60}"
for _ in $(seq 1 "$max_attempts"); do
if curl -fsS "$url" >/dev/null 2>&1; then
echo "$label ready: $url"
return 0
fi
sleep 2
done
echo "$label did not become ready: $url" >&2
return 1
}
wait_for_url "http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/health" "control plane"
wait_for_url "http://localhost:${BETTERNAS_NODE_AGENT_PORT}/health" "node agent"
wait_for_url "${NEXTCLOUD_BASE_URL}/status.php" "nextcloud"

58
scripts/lib/runtime-env.sh Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
compose_file="$repo_root/infra/docker/compose.dev.yml"
default_env_file="$repo_root/.env.agent"
env_file="${BETTERNAS_ENV_FILE:-$default_env_file}"
if [[ -f "$env_file" ]]; then
set -a
# shellcheck disable=SC1090
source "$env_file"
set +a
fi
: "${BETTERNAS_CLONE_NAME:=betternas-main}"
: "${COMPOSE_PROJECT_NAME:=betternas-${BETTERNAS_CLONE_NAME}}"
: "${BETTERNAS_CONTROL_PLANE_PORT:=3001}"
: "${BETTERNAS_NODE_AGENT_PORT:=3090}"
: "${BETTERNAS_NEXTCLOUD_PORT:=8080}"
: "${BETTERNAS_VERSION:=local-dev}"
: "${NEXTCLOUD_ADMIN_USER:=admin}"
: "${NEXTCLOUD_ADMIN_PASSWORD:=admin}"
if [[ -z "${BETTERNAS_EXPORT_PATH:-}" ]]; then
BETTERNAS_EXPORT_PATH="$repo_root/.state/$BETTERNAS_CLONE_NAME/export"
fi
if [[ "$BETTERNAS_EXPORT_PATH" != /* ]]; then
BETTERNAS_EXPORT_PATH="$repo_root/$BETTERNAS_EXPORT_PATH"
fi
: "${BETTERNAS_NODE_DIRECT_ADDRESS:=http://localhost:${BETTERNAS_NODE_AGENT_PORT}}"
: "${BETTERNAS_EXAMPLE_MOUNT_URL:=http://localhost:${BETTERNAS_NODE_AGENT_PORT}/dav/}"
: "${NEXTCLOUD_BASE_URL:=http://localhost:${BETTERNAS_NEXTCLOUD_PORT}}"
export repo_root
export compose_file
export env_file
export BETTERNAS_CLONE_NAME
export COMPOSE_PROJECT_NAME
export BETTERNAS_CONTROL_PLANE_PORT
export BETTERNAS_NODE_AGENT_PORT
export BETTERNAS_NEXTCLOUD_PORT
export BETTERNAS_EXPORT_PATH
export BETTERNAS_VERSION
export NEXTCLOUD_ADMIN_USER
export NEXTCLOUD_ADMIN_PASSWORD
export BETTERNAS_NODE_DIRECT_ADDRESS
export BETTERNAS_EXAMPLE_MOUNT_URL
export NEXTCLOUD_BASE_URL
mkdir -p "$BETTERNAS_EXPORT_PATH"
compose() {
docker compose -f "$compose_file" "$@"
}

51
scripts/setup-clones Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
parent_dir="$(cd "$repo_root/.." && pwd)"
repo_name="$(basename "$repo_root")"
sync_clone_script="$repo_root/scripts/sync-clone"
declare -A clone_ports=(
["betterNAS-runtime"]="41080 41090 41001"
["betterNAS-control"]="42080 42090 42001"
["betterNAS-node"]="43080 43090 43001"
)
clone_names=("betterNAS-runtime" "betterNAS-control" "betterNAS-node")
for clone_name in "${clone_names[@]}"; do
clone_dir="$parent_dir/$clone_name"
"$sync_clone_script" "$clone_dir"
read -r nextcloud_port node_agent_port control_plane_port <<<"${clone_ports[$clone_name]}"
cat >"$clone_dir/.env.agent" <<EOF
BETTERNAS_CLONE_NAME=${clone_name}
COMPOSE_PROJECT_NAME=${clone_name}
BETTERNAS_NEXTCLOUD_PORT=${nextcloud_port}
BETTERNAS_NODE_AGENT_PORT=${node_agent_port}
BETTERNAS_CONTROL_PLANE_PORT=${control_plane_port}
BETTERNAS_EXPORT_PATH=.state/${clone_name}/export
BETTERNAS_VERSION=local-dev
BETTERNAS_NODE_DIRECT_ADDRESS=http://localhost:${node_agent_port}
BETTERNAS_EXAMPLE_MOUNT_URL=http://localhost:${node_agent_port}/dav/
NEXTCLOUD_BASE_URL=http://localhost:${nextcloud_port}
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=admin
EOF
done
cat <<EOF
Clone setup complete under ${parent_dir}
Main repo: ${repo_name}
Runtime clone: ${parent_dir}/betterNAS-runtime
Control clone: ${parent_dir}/betterNAS-control
Node clone: ${parent_dir}/betterNAS-node
Next in each clone:
pnpm agent:bootstrap
pnpm agent:verify
EOF

39
scripts/sync-clone Executable file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [[ $# -ne 1 ]]; then
echo "usage: $0 /absolute/path/to/clone" >&2
exit 1
fi
clone_dir="$1"
if [[ "$clone_dir" != /* ]]; then
echo "clone path must be absolute: $clone_dir" >&2
exit 1
fi
if [[ ! -d "$clone_dir/.git" ]]; then
git clone --local --no-hardlinks "$repo_root" "$clone_dir"
fi
find "$clone_dir" -mindepth 1 -maxdepth 1 \
! -name .git \
! -name .env.agent \
-exec rm -rf {} +
tar \
--exclude=.git \
--exclude=.state \
--exclude=.turbo \
--exclude=node_modules \
--exclude=dist \
--exclude='apps/web/.next' \
-C "$repo_root" \
-cf - \
. | tar -C "$clone_dir" -xf -
echo "Synced working tree into $clone_dir"