Files
harmony/fleet/scripts/dev-enroll-device.sh
Jean-Gabriel Gill-Couture 6772cb27a4 docs(fleet): correct --admin-oidc-client-id (bare numeric, no @fleet suffix)
The client_id is the harmony-cli app's plain numeric Client ID — not
<num>@fleet, not the app name, not the app Resource ID. Fix the enroll
README example and the dev-enroll-device.sh comment accordingly.
2026-06-02 10:00:00 -04:00

97 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build the aarch64 agent + enroll one device into staging in one shot —
# the fast inner loop for iterating on the agent against a real Pi (or a
# Pi-equivalent VM), skipping the manual cross-build + long enroll command.
#
# It runs the SAME path the procedure documents, just wrapped:
# 1. cross-compile harmony-fleet-agent for $AGENT_TARGET (the binary
# that gets uploaded to /usr/local/bin/fleet-agent).
# 2. example_fleet_device_enroll — mints the device's Zitadel machine
# user + key, then runs FleetDeviceSetupScore over SSH to install
# podman, drop the config, upload the agent, and bring up
# fleet-agent.service (enabled --now → autostarts on boot).
#
# Full procedure + failure modes: examples/fleet_device_enroll/README.md
# Systemd unit / autostart: harmony/src/modules/fleet/setup_score.rs
#
# Usage (per-device + secret values come from env / a local .envrc, never
# committed — keep your Pi host, device id, and ids there):
# DEVICE_ID=pi-lab-01 PI_HOST=192.168.2.19 \
# AUDIENCE=<fleet-project-id> ADMIN_OIDC_CLIENT_ID=<numeric-client-id> \
# ./fleet/scripts/dev-enroll-device.sh
#
# SKIP_BUILD=1 ./fleet/scripts/dev-enroll-device.sh # reuse last agent build
# DEV_ON_DEVICE=1 ./fleet/scripts/dev-enroll-device.sh # run it ON the Pi (no SSH)
#
# Prerequisites:
# - A running staging install (Zitadel + NATS + callout + operator).
# - aarch64 cross toolchain for $AGENT_TARGET. gnu (default) uses the
# system aarch64-linux-gnu-gcc; for an older Pi OS prefer musl via
# `cross` (see the README's "Why musl, not gnu").
# - SSH access to $PI_HOST as $SSH_USER (unless DEV_ON_DEVICE=1).
# - A Zitadel SSO account with machine-user / role-grant / key perms
# (browser device-code), or HARMONY_ZITADEL_ADMIN_TOKEN set to skip it.
set -euo pipefail
# ── Required (no sane default — must identify the device + Zitadel app) ──
: "${DEVICE_ID:?set DEVICE_ID (RFC1123 subdomain, e.g. pi-lab-01)}"
: "${AUDIENCE:?set AUDIENCE (the fleet Zitadel project id)}"
: "${ADMIN_OIDC_CLIENT_ID:?set ADMIN_OIDC_CLIENT_ID (the harmony-cli app\'s numeric Client ID, e.g. 371683318111994677 — just the number, no suffix; NOT the app name 'harmony-cli', NOT the app Resource ID)}"
# ── Tunable (staging defaults) ──────────────────────────────────────────
SSH_USER="${SSH_USER:-pi}"
ISSUER_URL="${ISSUER_URL:-https://sso-staging.cb1.nationtech.io}"
NATS_URL="${NATS_URL:-wss://nats-fleet-staging.cb1.nationtech.io}"
LABELS="${LABELS:-group=lab,arch=aarch64}"
AGENT_TARGET="${AGENT_TARGET:-aarch64-unknown-linux-gnu}"
SKIP_BUILD="${SKIP_BUILD:-0}"
DEV_ON_DEVICE="${DEV_ON_DEVICE:-0}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$REPO_ROOT"
# Pick up a local .envrc (PI_HOST, ids, admin token, …) if present.
if [[ -f "$REPO_ROOT/.envrc" ]]; then
# shellcheck disable=SC1091
source "$REPO_ROOT/.envrc"
fi
AGENT_BIN="target/${AGENT_TARGET}/release/harmony-fleet-agent"
if [[ "$SKIP_BUILD" != "1" ]]; then
echo "==> [1/2] cross-compile agent for ${AGENT_TARGET}"
# The gnu target needs the system aarch64 cross-gcc as linker (no
# ~/.cargo/config.toml target stanza in this repo). Harmless for other
# targets — cargo only reads the matching var.
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER:-aarch64-linux-gnu-gcc}"
export CC_aarch64_unknown_linux_gnu="${CC_aarch64_unknown_linux_gnu:-aarch64-linux-gnu-gcc}"
export CXX_aarch64_unknown_linux_gnu="${CXX_aarch64_unknown_linux_gnu:-aarch64-linux-gnu-g++}"
export AR_aarch64_unknown_linux_gnu="${AR_aarch64_unknown_linux_gnu:-aarch64-linux-gnu-ar}"
cargo build --release --target "$AGENT_TARGET" -p harmony-fleet-agent
else
echo "==> [1/2] SKIP_BUILD=1, reusing ${AGENT_BIN}"
fi
[[ -f "$AGENT_BIN" ]] || { echo "agent binary not found: $AGENT_BIN (unset SKIP_BUILD?)"; exit 1; }
echo "==> [2/2] enroll ${DEVICE_ID}"
# DEV_ON_DEVICE: run ON the Pi (ansible local connection); otherwise SSH.
target_args=(--target "ssh://${SSH_USER}@${PI_HOST:?set PI_HOST or DEV_ON_DEVICE=1}")
[[ "$DEV_ON_DEVICE" == "1" ]] && target_args=()
cargo run -q -p example_fleet_device_enroll -- \
"${target_args[@]}" \
--device-id "$DEVICE_ID" \
--issuer-url "$ISSUER_URL" \
--audience "$AUDIENCE" \
--nats-url "$NATS_URL" \
--admin-oidc-client-id "$ADMIN_OIDC_CLIENT_ID" \
--agent-binary "$AGENT_BIN" \
--labels "$LABELS"
echo
echo "Enrolled ${DEVICE_ID}. Verify the heartbeat:"
echo " kubectl get fleetdev ${DEVICE_ID} # Device CR"
echo " nats kv get fleet-device-info ${DEVICE_ID}"