#!/usr/bin/env bash
# Peeryx Flow Collector — versioned, signed installation
set -Eeuo pipefail
umask 077
PFC_VERSION=1.1.0
BASE="https://peeryx.com/downloads/flow-collector/$PFC_VERSION"
CHECK=0
BUNDLE=''
while (($#)); do
  case "$1" in
    --check) CHECK=1; shift ;;
    --bundle) BUNDLE="${2:?Missing local bundle directory}"; shift 2 ;;
    *) echo 'Usage: bash install.sh [--check] [--bundle DIRECTORY]' >&2; exit 2 ;;
  esac
done
[[ $(id -u) == 0 ]] || { echo 'Run with sudo.' >&2; exit 1; }
[[ -f /etc/os-release ]] || { echo 'Unsupported operating system.' >&2; exit 1; }
. /etc/os-release
case "$ID:$VERSION_ID" in debian:12|debian:13|ubuntu:24.04) ;; *) echo 'Use Debian 12/13 or Ubuntu 24.04.' >&2; exit 1;; esac
case "$(uname -m)" in x86_64) ARCH=amd64;; aarch64|arm64) ARCH=arm64;; *) echo 'Use x86-64 or ARM64.' >&2; exit 1;; esac
[[ -d /run/systemd/system ]] || { echo 'A systemd host is required.' >&2; exit 1; }
echo "Peeryx Flow Collector $PFC_VERSION · $ID $VERSION_ID · $ARCH"
if [[ -f /var/lib/peeryx-collector/incidents.json ]]; then
  python3 - <<'PY'
import json
s=json.load(open('/var/lib/peeryx-collector/incidents.json'))
if any(r.get('phase') in {'announcing','diverted','restoring'} for r in s.get('incidents',{}).values()):
 raise SystemExit('An active diversion exists. Finish recovery before installation or upgrade.')
PY
fi
if [[ -f /etc/peeryx-collector/config.json ]] && ((!CHECK)); then
  python3 - <<'PYMODE'
import json
if json.load(open('/etc/peeryx-collector/config.json')).get('enabled'):
 raise SystemExit('Switch to observation with sudo peeryx-collector observe before upgrading. Active diversions must recover first.')
PYMODE
fi
if ((CHECK)); then
  echo 'OS/architecture checks passed. Recommended: 2 CPU cores, 8 GB RAM, 20 GB disk, 1 GbE.'
  echo 'Required packages: python3, curl, ca-certificates, openssl, bird2, iproute2.'
  exit 0
fi
# Never replace or restart an existing routing daemon on a shared router host.
if [[ ! -L /opt/peeryx-collector/current ]] && command -v ss >/dev/null && ss -H -ltn | awk '$4 ~ /:179$/ {found=1} END {exit !found}'; then
  echo 'TCP 179 is already in use. Install on a dedicated VM; existing routing is left unchanged.' >&2; exit 1
fi
work=$(mktemp -d /var/tmp/peeryx-flow-install.XXXXXXXX)
cleanup() { rm -rf -- "$work"; }
trap cleanup EXIT
# Prevent only package-triggered starts in this installation, preserving an
# administrator's existing policy-rc.d. No installed service is stopped.
policy_created=0
restore_policy() { if ((policy_created)); then rm -f /usr/sbin/policy-rc.d; policy_created=0; fi; }
trap 'restore_policy; cleanup' EXIT
if ! command -v bird >/dev/null || ! command -v curl >/dev/null || ! command -v openssl >/dev/null || ! command -v python3 >/dev/null || ! command -v ip >/dev/null || [[ ! -s /etc/ssl/certs/ca-certificates.crt ]]; then
  if [[ ! -e /usr/sbin/policy-rc.d ]]; then
    printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d; chmod 755 /usr/sbin/policy-rc.d; policy_created=1
  fi
  export DEBIAN_FRONTEND=noninteractive
  apt-get update
  apt-get install -y --no-install-recommends python3 curl ca-certificates openssl bird2 iproute2
  restore_policy
fi
PACKAGE="peeryx-flow-collector-$PFC_VERSION-linux-$ARCH.tar.gz"
for file in SHA256SUMS SHA256SUMS.sig "$PACKAGE"; do
  if [[ -n "$BUNDLE" ]]; then cp -- "$BUNDLE/$file" "$work/$file";
  else curl --proto '=https' --tlsv1.2 --fail --show-error --silent --max-time 180 "$BASE/$file" -o "$work/$file"; fi
done
cat > "$work/release-key.pem" <<'KEY'
-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0AYrHedq2N1UjYfwPNZv
yCICfvTqXcmzcFtTaGHuJoNj6sMXLxkU5Bc+6Hb1cMuctkSbs29GvrvuNUD14W12
K0Ek2/pfkLWUgAm4jKH7hpq9ZPCKnqQOeY966640EKEBb3jubTYF8u7ES71u+gE7
ZWqt6U2WnQ2mr++D2MuKds5jb0AGXqFHXnVYXlPAt6N56HBxI2ks6ORUK+n/AZ8W
jg9t6CvJYuX8+Ru0O9SX93L1RKRSobnh5TgrUP/xrB6+0owArahpT2KPju8PfHfB
aqIezCMcubbFHBKw7b/6FdzNhOGigtfYBAi9uGCDutNgkjHi+bcHWV04MsvQGoPE
OMoN6FEZNUDMqbmOL8AYIZ0RcFutSVxUGi2GMoLq1+4u8nHxBU1kydVvlbhbC1Ea
v7YH1yNqUTiD8GwAK5ULdTjaAHmVtXHdB2wP7uy4Pv8qiLWUvUdk08uBEtCAv7UM
ATm+8bX7v7/odFhKvOZSdaAH8jnW8cX7SLlelo+3TOXJAgMBAAE=
-----END PUBLIC KEY-----
KEY
openssl dgst -sha256 -verify "$work/release-key.pem" -signature "$work/SHA256SUMS.sig" "$work/SHA256SUMS" >/dev/null
python3 - "$work" "$PACKAGE" <<'PY'
import hashlib,pathlib,sys,tarfile
root=pathlib.Path(sys.argv[1]);name=sys.argv[2]
entries={}
for line in (root/'SHA256SUMS').read_text().splitlines():
 digest,filename=line.split(maxsplit=1);entries[filename.lstrip('*')]=digest
if hashlib.sha256((root/name).read_bytes()).hexdigest()!=entries.get(name):raise SystemExit('Package checksum mismatch')
with tarfile.open(root/name,'r:gz') as archive:
 for member in archive.getmembers():
  p=pathlib.PurePosixPath(member.name)
  if p.is_absolute() or '..' in p.parts or not (member.isfile() or member.isdir()):raise SystemExit('Unsafe package member')
 archive.extractall(root/'package')
PY
id peeryx-flow >/dev/null 2>&1 || useradd --system --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin peeryx-flow
install -d -m 755 /opt/peeryx-collector/releases /etc/peeryx-collector
release="/opt/peeryx-collector/releases/$PFC_VERSION"
if [[ -e "$release" ]]; then
  echo "Release $PFC_VERSION already exists; verifying it before reuse."
  diff -qr "$work/package" "$release" >/dev/null || { echo 'Installed release differs; refusing to overwrite.' >&2; exit 1; }
else
  cp -a "$work/package" "$release"
  chown -R root:root "$release"; chmod -R go-w "$release"
fi
old=$(readlink -f /opt/peeryx-collector/current || true)
ln -sfn "$release" /opt/peeryx-collector/current.new
mv -Tf /opt/peeryx-collector/current.new /opt/peeryx-collector/current
install -m 644 "$release/packaging/peeryx-collector.tmpfiles" /etc/tmpfiles.d/peeryx-collector.conf
systemd-tmpfiles --create /etc/tmpfiles.d/peeryx-collector.conf
for unit in peeryx-collector peeryx-collector-bgp peeryx-collector-detector; do
  install -m 644 "$release/packaging/$unit.service" "/etc/systemd/system/$unit.service"
done
cat > /usr/local/sbin/peeryx-collector <<'CLI'
#!/bin/sh
exec /usr/bin/python3 -B /opt/peeryx-collector/current/runtime/collector.py "$@"
CLI
chmod 755 /usr/local/sbin/peeryx-collector
systemctl daemon-reload
if [[ -f /etc/peeryx-collector/config.json ]]; then
  if ! peeryx-collector apply; then
    if [[ -n "$old" && "$old" != "$release" ]]; then
      ln -sfn "$old" /opt/peeryx-collector/current.rollback; mv -Tf /opt/peeryx-collector/current.rollback /opt/peeryx-collector/current
      peeryx-collector apply || true
    fi
    echo 'Configuration validation failed; inspect the error before continuing.' >&2; exit 1
  fi
else
  echo 'Installed. Next: sudo peeryx-collector setup'
fi
echo 'Guide: https://peeryx.com/client (open your service → Peeryx Flow Collector)'
