#!/bin/sh # Installs the Fwoop agent and connects this server to your workspace. # # curl -fsSL https://get.fwoop.dev | sh -s -- --token # # It downloads the newest release for this server's architecture from # get.fwoop.dev (deploy/get) and checks its SHA-256, installs it as # /usr/local/bin/fwoop, creates the fwoop system user (in the docker group when Docker is # installed), enrolls the server, then installs and starts fwoop-agent.service. Configuration # goes to /etc/fwoop/agent.yaml, state to /var/lib/fwoop. The service runs its own copy of the # binary, /var/lib/fwoop/bin/current/fwoop, which the agent replaces when it updates itself. set -eu usage() { cat <<'EOF' Usage: install.sh --token [options] --token one-time token from Servers → Connect server (required) --name server name in Fwoop (default: the hostname) --labels labels, e.g. role=api,region=eu --project project to join (default: the workspace's default project) --proxy HTTPS proxy for the control-plane connection --plane control plane URL (default: https://connect.eonfluxtech.cloud) --version install this release instead of the newest For development: --binary install this fwoop binary instead of downloading one --insecure allow a plain-HTTP control plane --update-key also accept agent updates signed with this key (base64) EOF } # Where releases are; FWOOP_RELEASES_URL replaces it for a test. releases=${FWOOP_RELEASES_URL:-https://get.fwoop.dev/releases} default_plane=https://connect.eonfluxtech.cloud bin=/usr/local/bin/fwoop unit=/etc/systemd/system/fwoop-agent.service fail() { printf 'fwoop install: %s\n' "$*" >&2 exit 1 } token='' name='' labels='' project='' proxy='' binary='' plane='' insecure='' update_key='' release='' while [ $# -gt 0 ]; do case $1 in --insecure) insecure=1 ;; -h | --help) usage exit 0 ;; --*=*) opt=${1%%=*} value=${1#*=} shift set -- "$opt" "$value" "$@" continue ;; --token | --name | --labels | --project | --proxy | --binary | --plane | --update-key | --version) [ $# -ge 2 ] || fail "$1 needs a value" case $1 in --token) token=$2 ;; --name) name=$2 ;; --labels) labels=$2 ;; --project) project=$2 ;; --proxy) proxy=$2 ;; --binary) binary=$2 ;; --plane) plane=$2 ;; --update-key) update_key=$2 ;; --version) release=$2 ;; esac shift ;; *) fail "unknown option $1 (see --help)" ;; esac shift done [ -n "$token" ] || fail "--token is required: get one from Servers → Connect server" [ -n "$plane" ] || plane=$default_plane case $release in '' | v[0-9]*) ;; *) fail "--version takes a release such as v1.2.3" ;; esac download_url=$releases/${release:-latest} [ "$(uname -s)" = Linux ] || fail "the agent runs on Linux only" [ "$(id -u)" -eq 0 ] || fail "run this as root, e.g. curl -fsSL https://get.fwoop.dev | sudo sh -s -- --token ..." [ -d /run/systemd/system ] || fail "systemd isn't running here, and the agent needs it" case $(uname -m) in x86_64 | amd64) arch=amd64 ;; aarch64 | arm64) arch=arm64 ;; *) fail "unsupported architecture $(uname -m): the agent runs on amd64 and arm64" ;; esac tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT # 1. The binary if [ -n "$binary" ]; then cp "$binary" "$tmp/fwoop" else command -v curl >/dev/null 2>&1 || fail "curl is required" echo "Downloading fwoop ${release:-(the newest release)} for linux/$arch" curl -fsSL -o "$tmp/fwoop" "$download_url/fwoop-linux-$arch" || fail "download failed: $download_url/fwoop-linux-$arch" curl -fsSL -o "$tmp/SHA256SUMS" "$download_url/SHA256SUMS" || fail "checksum download failed" # TLS and the checksum vouch for the download here. The release's Ed25519 signature is # what the agent checks when it updates itself: verifying it in sh would need tools a # fresh server may not have. expected=$(awk -v f="fwoop-linux-$arch" '$2 == f { print $1 }' "$tmp/SHA256SUMS") [ -n "$expected" ] || fail "no checksum for fwoop-linux-$arch" actual=$(sha256sum "$tmp/fwoop" | awk '{ print $1 }') [ "$expected" = "$actual" ] || fail "checksum mismatch for fwoop-linux-$arch" fi install -m 0755 "$tmp/fwoop" "$bin" echo "Installed $("$bin" version)" # 2. The fwoop user if ! id fwoop >/dev/null 2>&1; then nologin=$(command -v nologin || echo /bin/false) useradd --system --home-dir /var/lib/fwoop --no-create-home --shell "$nologin" fwoop fi if getent group docker >/dev/null 2>&1; then usermod -aG docker fwoop else docker_missing=1 fi # 3. Enrollment set -- agent enroll --token "$token" if [ -n "$name" ]; then set -- "$@" --name "$name"; fi if [ -n "$labels" ]; then set -- "$@" --labels "$labels"; fi if [ -n "$project" ]; then set -- "$@" --project "$project"; fi if [ -n "$proxy" ]; then set -- "$@" --proxy "$proxy"; fi if [ -n "$plane" ]; then set -- "$@" --plane "$plane"; fi if [ -n "$insecure" ]; then set -- "$@" --insecure; fi if [ -n "$update_key" ]; then set -- "$@" --update-key "$update_key"; fi "$bin" "$@" || fail "enrollment failed" # The service's own copy, which the agent may replace with another version. version=$("$bin" version --json | sed -n 's/^ *"version": *"\([A-Za-z0-9._+-]*\)".*/\1/p') [ -n "$version" ] || version=dev install -d -m 0755 /var/lib/fwoop/bin install -D -m 0755 "$bin" "/var/lib/fwoop/bin/$version/fwoop" ln -sfn "$version" /var/lib/fwoop/bin/current chown root:fwoop /etc/fwoop/agent.yaml chown -R fwoop:fwoop /var/lib/fwoop # 4. The service "$bin" agent unit >"$unit" systemctl daemon-reload systemctl enable fwoop-agent.service >/dev/null 2>&1 systemctl restart fwoop-agent.service echo "Waiting for the agent to connect" i=0 while [ "$i" -lt 20 ]; do if status=$("$bin" agent status 2>/dev/null); then echo "$status" if [ -n "${docker_missing-}" ]; then echo "Docker isn't installed. Services need it: after installing Docker, run" echo " usermod -aG docker fwoop && systemctl restart fwoop-agent" fi exit 0 fi i=$((i + 1)) sleep 1 done "$bin" agent status || true fail "the agent is installed but hasn't connected yet; see journalctl -u fwoop-agent"