Compare commits
No commits in common. "ec33948b8ccd5e3c2be2e7bcac16a3b651d7506d" and "8d04472ec171e6dd40d23a2fa8fcd5b07493bba4" have entirely different histories.
ec33948b8c
...
8d04472ec1
3 changed files with 86 additions and 444 deletions
|
|
@ -42,40 +42,27 @@ jobs:
|
||||||
goarch: ${{ matrix.goarch }}
|
goarch: ${{ matrix.goarch }}
|
||||||
binari: ${{ matrix.binaries }}
|
binari: ${{ matrix.binaries }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
# Scripts et units systemd publiés comme assets de release : deploy.sh les
|
upload-scripts:
|
||||||
# installe depuis la release, plus depuis la branche. Ajouter une entrée ici
|
|
||||||
# suffit à livrer un nouveau fichier.
|
|
||||||
upload-assets:
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
needs: [set-release-target]
|
needs: [set-release-target]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
script:
|
||||||
- path: scripts/run-dnsmasq-in-netns.sh
|
- run-dnsmasq-in-netns.sh
|
||||||
name: run-dnsmasq-in-netns.sh
|
|
||||||
- path: systemd/agent.service
|
|
||||||
name: agent.service
|
|
||||||
- path: systemd/dnsmasq@.service
|
|
||||||
name: dnsmasq@.service
|
|
||||||
- path: systemd/metadata@.service
|
|
||||||
name: metadata@.service
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Move asset
|
- name: Move asset
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "dist"
|
mkdir -p "dist"
|
||||||
cp "${{ matrix.path }}" dist/
|
cp scripts/${{ matrix.script }} dist/
|
||||||
- name: Upload asset
|
- name: Upload script
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}-${{ needs.set-release-target.outputs.release_cible }}
|
name: ${{ matrix.script }}-${{ needs.set-release-target.outputs.release_cible }}
|
||||||
path: dist/${{ matrix.name }}
|
path: dist/${{ matrix.script }}
|
||||||
prerelease:
|
prerelease:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
# upload-assets est dans les needs : sans lui, le job release peut appeler
|
needs: [set-release-target, build]
|
||||||
# download-artifact avant la fin des uploads et publier une release
|
|
||||||
# incomplète (scripts et units manquants de façon non déterministe).
|
|
||||||
needs: [set-release-target, build, upload-assets]
|
|
||||||
uses: ./.forgejo/workflows/release.yml
|
uses: ./.forgejo/workflows/release.yml
|
||||||
with:
|
with:
|
||||||
tag: ${{ needs.set-release-target.outputs.release_cible }}
|
tag: ${{ needs.set-release-target.outputs.release_cible }}
|
||||||
|
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Préparation d'un host au profil kvm : paquets, kernel, réseau de base.
|
|
||||||
#
|
|
||||||
# Ce fichier n'est pas exécutable seul : il est *sourcé* par deploy.sh quand
|
|
||||||
# --bootstrap est demandé, et réutilise ses helpers (run, info, warn, die,
|
|
||||||
# exec_with_dry_run, FLAGS_TRUE). Conséquences :
|
|
||||||
# - aucun effet de bord au chargement : uniquement des définitions,
|
|
||||||
# - aucune variable globale de deploy.sh redéfinie ici (SCRIPT_PATH, TAG,
|
|
||||||
# BIN_PATH, ASSETS…) : le sourcing les écraserait,
|
|
||||||
# - pas de `set -e` ni de garde `BASH_SOURCE` en fin de fichier.
|
|
||||||
#
|
|
||||||
# Contrat : tout bootstrap_<profil>.sh expose bootstrap_host() avec cette
|
|
||||||
# signature. deploy.sh n'a rien à savoir du contenu du profil.
|
|
||||||
#
|
|
||||||
# L'host est stateless (root sur tmpfs) : rien de ce qui suit ne survit à un
|
|
||||||
# reboot, deploy.sh --bootstrap est rejoué à chaque démarrage.
|
|
||||||
|
|
||||||
KVM_PACKAGES="qemu-system-x86 ovmf dnsmasq ebtables iptables nfs-common jq curl"
|
|
||||||
|
|
||||||
kvm_packages () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local WITH_PACKAGES="${2}"
|
|
||||||
|
|
||||||
[[ ${WITH_PACKAGES} -eq ${FLAGS_TRUE} ]] || { info "paquets : ignorés (--nopackages)"; return 0; }
|
|
||||||
|
|
||||||
info "paquets"
|
|
||||||
run "${DRY_RUN}" "DEBIAN_FRONTEND=noninteractive apt-get update"
|
|
||||||
run "${DRY_RUN}" "DEBIAN_FRONTEND=noninteractive apt-get install -y ${KVM_PACKAGES}"
|
|
||||||
|
|
||||||
# Le dnsmasq système prendrait le port 53 en concurrence des instances
|
|
||||||
# dnsmasq@ lancées par l'agent dans les netns.
|
|
||||||
run "${DRY_RUN}" "systemctl disable --now dnsmasq.service || true"
|
|
||||||
run "${DRY_RUN}" "systemctl mask dnsmasq.service"
|
|
||||||
}
|
|
||||||
|
|
||||||
kvm_kernel () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
|
|
||||||
info "kernel"
|
|
||||||
|
|
||||||
# modprobe AVANT sysctl : les clés net.bridge.* n'existent pas tant que
|
|
||||||
# br_netfilter n'est pas chargé, et sysctl --system échouerait.
|
|
||||||
run "${DRY_RUN}" "modprobe br_netfilter"
|
|
||||||
run "${DRY_RUN}" "echo br_netfilter > /etc/modules-load.d/two.conf"
|
|
||||||
|
|
||||||
# bridge-nf-call-iptables est requis par le DNAT metadata (169.254.169.254,
|
|
||||||
# cf. internal/iptables) : sans lui, iptables ne voit pas le trafic bridgé
|
|
||||||
# des VMs. Contrepartie : tout le trafic inter-VM traverse les tables nat.
|
|
||||||
run "${DRY_RUN}" "printf 'net.ipv4.ip_forward = 1\nnet.bridge.bridge-nf-call-iptables = 1\n' > /etc/sysctl.d/90-two.conf"
|
|
||||||
run "${DRY_RUN}" "sysctl --system >/dev/null"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Bridge vide, up, sans STP.
|
|
||||||
kvm_ensure_bridge () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local NAME="${2}"
|
|
||||||
|
|
||||||
if ! ip link show dev "${NAME}" >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
run "${DRY_RUN}" "ip link add name '${NAME}' type bridge"
|
|
||||||
fi
|
|
||||||
run "${DRY_RUN}" "ip link set dev '${NAME}' type bridge stp_state 0"
|
|
||||||
run "${DRY_RUN}" "ip link set up dev '${NAME}'"
|
|
||||||
}
|
|
||||||
|
|
||||||
kvm_network () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local WITH_NETWORK="${2}"
|
|
||||||
local UPLINK="${3}"
|
|
||||||
local BRIDGE="${4}"
|
|
||||||
local PUBLIC_BRIDGE="${5}"
|
|
||||||
local ROLLBACK_DELAY="${6}"
|
|
||||||
local MASTER ADDR_JSON IP PREFIX GW MIGRATE MIGRATE_SCRIPT
|
|
||||||
|
|
||||||
[[ ${WITH_NETWORK} -eq ${FLAGS_TRUE} ]] || { info "réseau : ignoré (--nonetwork)"; return 0; }
|
|
||||||
|
|
||||||
info "réseau"
|
|
||||||
|
|
||||||
# Bridge public : créé à vide, réservé pour un usage futur. Aucune adresse,
|
|
||||||
# aucun esclave, pas référencé dans la config agent.
|
|
||||||
kvm_ensure_bridge "${DRY_RUN}" "${PUBLIC_BRIDGE}"
|
|
||||||
|
|
||||||
MASTER=""
|
|
||||||
[[ ${DRY_RUN} -ne ${FLAGS_TRUE} ]] && MASTER=$(ip -j link show dev "${UPLINK}" | jq -r '.[0].master // ""')
|
|
||||||
if [[ "${MASTER}" == "${BRIDGE}" ]]
|
|
||||||
then
|
|
||||||
info " ${UPLINK} déjà esclave de ${BRIDGE} — migration ignorée"
|
|
||||||
kvm_ensure_bridge "${DRY_RUN}" "${BRIDGE}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# IP, préfixe et gateway dérivés de l'uplink : rien en dur. Le filtre
|
|
||||||
# inet/global évite de tomber sur une IPv6 ou une adresse de lien.
|
|
||||||
if [[ ${DRY_RUN} -eq ${FLAGS_TRUE} ]]
|
|
||||||
then
|
|
||||||
IP="<ip(${UPLINK})>"; PREFIX="<prefixlen>"; GW="<gateway>"
|
|
||||||
else
|
|
||||||
ADDR_JSON=$(ip -j addr show dev "${UPLINK}")
|
|
||||||
IP=$(echo "${ADDR_JSON}" | jq -r '[.[0].addr_info[] | select(.family=="inet" and .scope=="global")][0].local // ""')
|
|
||||||
PREFIX=$(echo "${ADDR_JSON}" | jq -r '[.[0].addr_info[] | select(.family=="inet" and .scope=="global")][0].prefixlen // ""')
|
|
||||||
GW=$(ip -j route show default dev "${UPLINK}" | jq -r '.[0].gateway // ""')
|
|
||||||
|
|
||||||
[[ -n "${IP}" && -n "${PREFIX}" ]] || die "aucune adresse IPv4 globale sur ${UPLINK}"
|
|
||||||
[[ -n "${GW}" ]] || die "aucune route par défaut via ${UPLINK}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info " ${UPLINK} : ${IP}/${PREFIX} gw ${GW} → ${BRIDGE}"
|
|
||||||
|
|
||||||
# Cette étape coupe le réseau de l'host si elle échoue à mi-parcours, sans
|
|
||||||
# console de secours. Deux garde-fous :
|
|
||||||
# - un rollback armé avant d'y toucher : rien n'étant écrit sur disque, un
|
|
||||||
# reboot ramène la configuration d'origine. Désarmé si le test passe.
|
|
||||||
# - la séquence tourne sous systemd et non dans la session SSH : une
|
|
||||||
# coupure de SSH ne l'interrompt plus à mi-chemin.
|
|
||||||
run "${DRY_RUN}" "systemctl stop two-net-rollback.timer 2>/dev/null || true"
|
|
||||||
run "${DRY_RUN}" "systemd-run --collect --unit=two-net-rollback --on-active=${ROLLBACK_DELAY} systemctl reboot"
|
|
||||||
|
|
||||||
MIGRATE=$(cat <<EOF
|
|
||||||
set -e
|
|
||||||
ip link show dev '${BRIDGE}' >/dev/null 2>&1 || ip link add name '${BRIDGE}' type bridge
|
|
||||||
ip link set dev '${BRIDGE}' type bridge stp_state 0
|
|
||||||
ip link set up dev '${BRIDGE}'
|
|
||||||
ip link set '${UPLINK}' master '${BRIDGE}'
|
|
||||||
ip addr add '${IP}/${PREFIX}' dev '${BRIDGE}'
|
|
||||||
ip route replace default via '${GW}' dev '${BRIDGE}'
|
|
||||||
ip addr del '${IP}/${PREFIX}' dev '${UPLINK}'
|
|
||||||
pkill dhclient || true
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ ${DRY_RUN} -eq ${FLAGS_TRUE} ]]
|
|
||||||
then
|
|
||||||
echo "# systemd-run --wait --collect --service-type=oneshot --unit=two-net-migrate /bin/bash <<'EOF'"
|
|
||||||
echo "${MIGRATE}" | sed -e 's/^/# /'
|
|
||||||
echo "# EOF"
|
|
||||||
else
|
|
||||||
MIGRATE_SCRIPT=$(mktemp /run/two-net-migrate.XXXXXX)
|
|
||||||
printf '%s\n' "${MIGRATE}" > "${MIGRATE_SCRIPT}"
|
|
||||||
systemctl reset-failed two-net-migrate.service 2>/dev/null || true
|
|
||||||
systemd-run --wait --collect --service-type=oneshot --unit=two-net-migrate \
|
|
||||||
/bin/bash "${MIGRATE_SCRIPT}" \
|
|
||||||
|| die "migration réseau échouée — rollback armé dans ${ROLLBACK_DELAY}s (reboot)"
|
|
||||||
rm -f "${MIGRATE_SCRIPT}"
|
|
||||||
|
|
||||||
# Vérification de connectivité avant de désarmer : seul critère qui
|
|
||||||
# distingue un succès d'un host qu'on vient d'isoler.
|
|
||||||
ping -c 2 -W 2 "${GW}" >/dev/null 2>&1 \
|
|
||||||
|| die "gateway ${GW} injoignable après migration — rollback armé dans ${ROLLBACK_DELAY}s (reboot)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
run "${DRY_RUN}" "systemctl stop two-net-rollback.timer 2>/dev/null || true"
|
|
||||||
info " rollback désarmé"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Point d'entrée appelé par deploy.sh --bootstrap.
|
|
||||||
bootstrap_host () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local WITH_PACKAGES="${2}"
|
|
||||||
local WITH_NETWORK="${3}"
|
|
||||||
local UPLINK="${4}"
|
|
||||||
local BRIDGE="${5}"
|
|
||||||
local PUBLIC_BRIDGE="${6}"
|
|
||||||
local ROLLBACK_DELAY="${7}"
|
|
||||||
|
|
||||||
kvm_packages "${DRY_RUN}" "${WITH_PACKAGES}"
|
|
||||||
kvm_kernel "${DRY_RUN}"
|
|
||||||
kvm_network "${DRY_RUN}" "${WITH_NETWORK}" "${UPLINK}" "${BRIDGE}" "${PUBLIC_BRIDGE}" "${ROLLBACK_DELAY}"
|
|
||||||
}
|
|
||||||
338
scripts/deploy.sh
Executable file → Normal file
338
scripts/deploy.sh
Executable file → Normal file
|
|
@ -10,12 +10,6 @@ case "${unameOut}" in
|
||||||
esac
|
esac
|
||||||
|
|
||||||
SCRIPT_PATH="scripts/deploy.sh"
|
SCRIPT_PATH="scripts/deploy.sh"
|
||||||
UNIT_DIR="/etc/systemd/system"
|
|
||||||
SCRIPTS_DIR="/opt/two/scripts"
|
|
||||||
|
|
||||||
info () { echo "== ${1}"; }
|
|
||||||
warn () { echo "!! ${1}" >&2; }
|
|
||||||
die () { echo "!! ${1}" >&2; exit 1; }
|
|
||||||
|
|
||||||
exec_with_dry_run () {
|
exec_with_dry_run () {
|
||||||
if [[ ${1} -eq ${FLAGS_TRUE} ]]; then
|
if [[ ${1} -eq ${FLAGS_TRUE} ]]; then
|
||||||
|
|
@ -24,7 +18,7 @@ exec_with_dry_run () {
|
||||||
eval "${2}" 2> /tmp/error || \
|
eval "${2}" 2> /tmp/error || \
|
||||||
{
|
{
|
||||||
echo -e "failed with following error";
|
echo -e "failed with following error";
|
||||||
local output; output=$(cat /tmp/error | sed -e "s/^/ error -> /g");
|
output=$(cat /tmp/error | sed -e "s/^/ error -> /g");
|
||||||
echo -e "${output}";
|
echo -e "${output}";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -32,295 +26,125 @@ exec_with_dry_run () {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
run () {
|
|
||||||
exec_with_dry_run "${1}" "${2}" || die "échec : ${2}"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_latest_script () {
|
check_latest_script () {
|
||||||
local REMOTE_URL="${1}"
|
REMOTE_URL="${1}"
|
||||||
local LOCAL_PATH="${2}"
|
LOCAL_PATH="${2}"
|
||||||
local REMOTE_SUM LOCAL_SUM
|
|
||||||
|
|
||||||
REMOTE_SUM=$(curl --silent "${REMOTE_URL}" | sha256sum)
|
REMOTE=$(curl --silent "${REMOTE_URL}" | sha256sum)
|
||||||
LOCAL_SUM=$(cat ${LOCAL_PATH} | sha256sum)
|
LOCAL=$(cat ${LOCAL_PATH} | sha256sum)
|
||||||
|
|
||||||
[[ "${REMOTE_SUM}" == "${LOCAL_SUM}" ]] || return 1
|
[[ "${REMOTE}" == "${LOCAL}" ]] || return 1
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Liste les units actives correspondant à un motif, une par ligne.
|
||||||
|
# Sortie vide si aucune ne tourne — ce n'est pas une erreur.
|
||||||
list_active_units () {
|
list_active_units () {
|
||||||
local PATTERN="${1}"
|
PATTERN="${1}"
|
||||||
systemctl list-units --state=active --no-legend --plain "${PATTERN}" 2>/dev/null \
|
systemctl list-units --state=active --no-legend --plain "${PATTERN}" 2>/dev/null \
|
||||||
| awk '{print $1}' || true
|
| awk '{print $1}' || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# Units non instanciables du profil (agent.service) : celles qu'on arrête et
|
|
||||||
# démarre nommément.
|
|
||||||
profile_main_units () {
|
|
||||||
local unit
|
|
||||||
for unit in $(profile_units "${1}")
|
|
||||||
do
|
|
||||||
case "${unit}" in
|
|
||||||
*@.service) continue ;;
|
|
||||||
*) echo "${unit}" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
active_instances () {
|
|
||||||
local unit
|
|
||||||
for unit in $(profile_units "${1}")
|
|
||||||
do
|
|
||||||
case "${unit}" in
|
|
||||||
*@.service) list_active_units "${unit%@.service}@*" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_services () {
|
stop_services () {
|
||||||
local DRY_RUN="${1}"
|
DRY_RUN="${1}"
|
||||||
local PROFILE="${2}"
|
METADATA_UNITS="${2}"
|
||||||
local INSTANCES="${3}"
|
DNSMASQ_UNITS="${3}"
|
||||||
local unit
|
|
||||||
|
|
||||||
for unit in $(profile_main_units "${PROFILE}")
|
# L'agent en premier : il pilote les autres units, on ne veut pas qu'il
|
||||||
do
|
# observe leur disparition.
|
||||||
run "${DRY_RUN}" "systemctl stop '${unit}'"
|
exec_with_dry_run "${DRY_RUN}" "systemctl stop agent.service"
|
||||||
done
|
|
||||||
|
|
||||||
for unit in ${INSTANCES}
|
for unit in ${METADATA_UNITS} ${DNSMASQ_UNITS}
|
||||||
do
|
do
|
||||||
run "${DRY_RUN}" "systemctl stop '${unit}'"
|
exec_with_dry_run "${DRY_RUN}" "systemctl stop '${unit}'"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
start_services () {
|
start_services () {
|
||||||
local DRY_RUN="${1}"
|
DRY_RUN="${1}"
|
||||||
local PROFILE="${2}"
|
METADATA_UNITS="${2}"
|
||||||
local INSTANCES="${3}"
|
DNSMASQ_UNITS="${3}"
|
||||||
local unit
|
|
||||||
|
|
||||||
for unit in ${INSTANCES}
|
# Ordre inverse de l'arrêt : les dépendances d'abord, l'agent en dernier.
|
||||||
|
for unit in ${DNSMASQ_UNITS} ${METADATA_UNITS}
|
||||||
do
|
do
|
||||||
exec_with_dry_run "${DRY_RUN}" "systemctl start '${unit}'" \
|
exec_with_dry_run "${DRY_RUN}" "systemctl start '${unit}'"
|
||||||
|| warn "démarrage de ${unit} en échec — poursuite"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
for unit in $(profile_main_units "${PROFILE}")
|
exec_with_dry_run "${DRY_RUN}" "systemctl start agent.service"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_binaries () {
|
||||||
|
DRY_RUN="${1}"
|
||||||
|
TAG="${2}"
|
||||||
|
GIT_SERVER="${3}"
|
||||||
|
REPO_PATH="${4}"
|
||||||
|
|
||||||
|
#'.[0].assets.[].browser_download_url'
|
||||||
|
[[ "${TAG}" == "" ]] && TAG=$(curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/?limit=1" | jq -r '.[0].tag_name')
|
||||||
|
echo "Deploy ${TAG} binaries"
|
||||||
|
|
||||||
|
BIN_PATH="/opt/two/${TAG}/bin/"
|
||||||
|
LN_PATH="/opt/two/bin/"
|
||||||
|
|
||||||
|
exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${BIN_PATH}\""
|
||||||
|
exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${LN_PATH}\""
|
||||||
|
|
||||||
|
ASSETS=$(curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/tags/${TAG}" | jq -c '.assets[]')
|
||||||
|
|
||||||
|
# Téléchargement d'abord, dans le répertoire versionné : aucun impact sur
|
||||||
|
# les services en cours, donc aucune raison de les arrêter pendant ce temps.
|
||||||
|
while read -r tmp
|
||||||
do
|
do
|
||||||
run "${DRY_RUN}" "systemctl start '${unit}'"
|
[[ -z "${tmp}" ]] && continue
|
||||||
done
|
BINARY_NAME=$(echo "${tmp}" | jq -r '.name')
|
||||||
}
|
BINARY_URL=$(echo "${tmp}" | jq -r '.browser_download_url')
|
||||||
|
exec_with_dry_run "${DRY_RUN}" "curl --silent '${BINARY_URL}' -o '${BIN_PATH}${BINARY_NAME}'"
|
||||||
|
exec_with_dry_run "${DRY_RUN}" "chmod +x '${BIN_PATH}${BINARY_NAME}'"
|
||||||
|
done <<< "${ASSETS}"
|
||||||
|
|
||||||
profile_units () {
|
# Les units actives sont relevées avant l'arrêt : c'est la seule façon de
|
||||||
case "${1}" in
|
# savoir lesquelles redémarrer ensuite (instances dnsmasq@ et metadata@).
|
||||||
kvm) echo "agent.service dnsmasq@.service metadata@.service" ;;
|
METADATA_UNITS=$(list_active_units 'metadata@*')
|
||||||
intel) echo "" ;;
|
DNSMASQ_UNITS=$(list_active_units 'dnsmasq@*')
|
||||||
*) return 1 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
profile_binaries () {
|
stop_services "${DRY_RUN}" "${METADATA_UNITS}" "${DNSMASQ_UNITS}"
|
||||||
case "${1}" in
|
|
||||||
kvm) echo "agent metadata run-dnsmasq-in-netns.sh" ;;
|
|
||||||
intel) echo "" ;;
|
|
||||||
*) return 1 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
profile_bootstrap () {
|
# Bascule des liens symboliques, services arrêtés : c'est la seule fenêtre
|
||||||
case "${1}" in
|
# d'indisponibilité réelle.
|
||||||
kvm) echo "bootstrap_kvm.sh" ;;
|
while read -r tmp
|
||||||
intel) echo "" ;;
|
|
||||||
*) return 1 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
run_bootstrap () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local PROFILE="${2}"
|
|
||||||
local GIT_SERVER="${3}"
|
|
||||||
local REPO_PATH="${4}"
|
|
||||||
local BRANCH="${5}"
|
|
||||||
local NAME BOOTSTRAP_URL
|
|
||||||
|
|
||||||
NAME=$(profile_bootstrap "${PROFILE}") || die "profil inconnu : ${PROFILE}"
|
|
||||||
[[ -n "${NAME}" ]] || { info "bootstrap : rien à préparer pour le profil ${PROFILE}"; return 0; }
|
|
||||||
|
|
||||||
BOOTSTRAP_URL="${GIT_SERVER}${REPO_PATH}raw/branch/${BRANCH}scripts/${NAME}"
|
|
||||||
info "bootstrap du profil ${PROFILE} (${SCRIPTS_DIR}/${NAME} ou ${BOOTSTRAP_URL})"
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
[[ -f "${SCRIPTS_DIR}/${NAME}" ]] && . "${SCRIPTS_DIR}/${NAME}" || eval "$(curl --silent --fail "${BOOTSTRAP_URL}")"
|
|
||||||
|
|
||||||
command -v bootstrap_host >/dev/null 2>&1 \
|
|
||||||
|| die "bootstrap_host() introuvable — ni ${SCRIPTS_DIR}/${NAME} ni ${BOOTSTRAP_URL} n'ont pu être chargés"
|
|
||||||
|
|
||||||
bootstrap_host "${DRY_RUN}" "${FLAGS_packages}" "${FLAGS_network}" \
|
|
||||||
"${FLAGS_uplink}" "${FLAGS_bridge}" "${FLAGS_pub_bridge}" "${FLAGS_rollback}"
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_tag () {
|
|
||||||
local TAG="${1}"
|
|
||||||
local GIT_SERVER="${2}"
|
|
||||||
local REPO_PATH="${3}"
|
|
||||||
|
|
||||||
[[ -n "${TAG}" ]] && { echo "${TAG}"; return 0; }
|
|
||||||
curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/?limit=1" | jq -r '.[0].tag_name'
|
|
||||||
}
|
|
||||||
|
|
||||||
release_assets () {
|
|
||||||
local GIT_SERVER="${1}"
|
|
||||||
local REPO_PATH="${2}"
|
|
||||||
local RELEASE_TAG="${3}"
|
|
||||||
|
|
||||||
curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/tags/${RELEASE_TAG}" | jq -c '.assets[]'
|
|
||||||
}
|
|
||||||
|
|
||||||
asset_name () {
|
|
||||||
echo "${1}" | jq -r --arg s "${2}" 'select(.name == $s or (.name | startswith($s + "_"))) | .name' | head -1
|
|
||||||
}
|
|
||||||
|
|
||||||
asset_url () {
|
|
||||||
echo "${1}" | jq -r --arg n "${2}" 'select(.name == $n) | .browser_download_url' | head -1
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch_assets () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local PROFILE="${2}"
|
|
||||||
local ASSETS="${3}"
|
|
||||||
local unit short UNIT_URL BIN_URL FULL_NAME
|
|
||||||
|
|
||||||
info "assets de la release ${TAG} pour le profil ${PROFILE}"
|
|
||||||
|
|
||||||
run "${DRY_RUN}" "mkdir -p '${BIN_PATH}'"
|
|
||||||
run "${DRY_RUN}" "mkdir -p '${UNIT_PATH}'"
|
|
||||||
run "${DRY_RUN}" "mkdir -p '${LN_PATH}'"
|
|
||||||
|
|
||||||
for unit in $(profile_units "${PROFILE}")
|
|
||||||
do
|
do
|
||||||
UNIT_URL=$(asset_url "${ASSETS}" "${unit}")
|
[[ -z "${tmp}" ]] && continue
|
||||||
[[ -n "${UNIT_URL}" ]] || die "unit absente de la release ${TAG} : ${unit}"
|
BINARY_NAME=$(echo "${tmp}" | jq -r '.name')
|
||||||
run "${DRY_RUN}" "curl --silent '${UNIT_URL}' -o '${UNIT_PATH}${unit}'"
|
BINARY_SHORT_NAME=$(echo "${BINARY_NAME}" | cut -d_ -f 1)
|
||||||
done
|
exec_with_dry_run "${DRY_RUN}" "rm -f '${LN_PATH}${BINARY_SHORT_NAME}'"
|
||||||
|
exec_with_dry_run "${DRY_RUN}" "ln -s '${BIN_PATH}${BINARY_NAME}' '${LN_PATH}${BINARY_SHORT_NAME}'"
|
||||||
|
done <<< "${ASSETS}"
|
||||||
|
|
||||||
for short in $(profile_binaries "${PROFILE}")
|
start_services "${DRY_RUN}" "${METADATA_UNITS}" "${DNSMASQ_UNITS}"
|
||||||
do
|
|
||||||
FULL_NAME=$(asset_name "${ASSETS}" "${short}")
|
|
||||||
[[ -n "${FULL_NAME}" ]] || die "exécutable absent de la release ${TAG} : ${short}"
|
|
||||||
BIN_URL=$(asset_url "${ASSETS}" "${FULL_NAME}")
|
|
||||||
run "${DRY_RUN}" "curl --silent '${BIN_URL}' -o '${BIN_PATH}${FULL_NAME}'"
|
|
||||||
run "${DRY_RUN}" "chmod +x '${BIN_PATH}${FULL_NAME}'"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
install_units () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local PROFILE="${2}"
|
|
||||||
local UNITS unit
|
|
||||||
|
|
||||||
UNITS=$(profile_units "${PROFILE}") || die "profil inconnu : ${PROFILE}"
|
|
||||||
[[ -n "${UNITS}" ]] || { info "units : aucune pour le profil ${PROFILE}"; return 0; }
|
|
||||||
|
|
||||||
info "units du profil ${PROFILE}"
|
|
||||||
|
|
||||||
for unit in ${UNITS}
|
|
||||||
do
|
|
||||||
if [[ ${DRY_RUN} -ne ${FLAGS_TRUE} ]] && [[ ! -f "${UNIT_PATH}${unit}" ]]
|
|
||||||
then
|
|
||||||
die "unit absente des assets de la release ${TAG} : ${unit}"
|
|
||||||
fi
|
|
||||||
run "${DRY_RUN}" "install -m 0644 '${UNIT_PATH}${unit}' '${UNIT_DIR}/${unit}'"
|
|
||||||
done
|
|
||||||
|
|
||||||
run "${DRY_RUN}" "systemctl daemon-reload"
|
|
||||||
|
|
||||||
for unit in ${UNITS}
|
|
||||||
do
|
|
||||||
case "${unit}" in
|
|
||||||
*@.service) continue ;;
|
|
||||||
esac
|
|
||||||
run "${DRY_RUN}" "systemctl enable '${unit}'"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
switch_binaries () {
|
|
||||||
local DRY_RUN="${1}"
|
|
||||||
local PROFILE="${2}"
|
|
||||||
local ASSETS="${3}"
|
|
||||||
local BINARIES INSTANCES short FULL_NAME
|
|
||||||
|
|
||||||
BINARIES=$(profile_binaries "${PROFILE}")
|
|
||||||
[[ -n "${BINARIES}" ]] || { info "bascule : aucun exécutable pour le profil ${PROFILE}"; return 0; }
|
|
||||||
|
|
||||||
info "bascule des binaires"
|
|
||||||
|
|
||||||
INSTANCES=$(active_instances "${PROFILE}")
|
|
||||||
|
|
||||||
stop_services "${DRY_RUN}" "${PROFILE}" "${INSTANCES}"
|
|
||||||
|
|
||||||
for short in ${BINARIES}
|
|
||||||
do
|
|
||||||
FULL_NAME=$(asset_name "${ASSETS}" "${short}")
|
|
||||||
run "${DRY_RUN}" "rm -f '${LN_PATH}${short}'"
|
|
||||||
run "${DRY_RUN}" "ln -s '${BIN_PATH}${FULL_NAME}' '${LN_PATH}${short}'"
|
|
||||||
done
|
|
||||||
|
|
||||||
start_services "${DRY_RUN}" "${PROFILE}" "${INSTANCES}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main () {
|
main () {
|
||||||
[[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)"
|
[[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)"
|
||||||
|
|
||||||
DEFINE_boolean 'dryrun' false 'Enable dry-run mode' 'd'
|
DEFINE_boolean 'dryrun' false 'Enable dry-run mode' 'd'
|
||||||
DEFINE_boolean 'up_script' true 'Upgrade script' 's'
|
DEFINE_boolean 'up_script' true 'Upgrade script' 's'
|
||||||
DEFINE_string 'git_server' 'https://git.g3e.fr/' 'Git Server' 'g'
|
DEFINE_string 'git_server' 'https://git.g3e.fr/' 'Git Server' 'g'
|
||||||
DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r'
|
DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r'
|
||||||
DEFINE_string 'branch' 'main/' 'Branch name' 'b'
|
DEFINE_string 'branch' 'main/' 'Branch name' 'b'
|
||||||
DEFINE_string 'tag' '' 'Tag name' 't'
|
DEFINE_string 'tag' '' 'Tag name' 't'
|
||||||
DEFINE_string 'profile' 'kvm' 'Host profile: kvm' 'p'
|
|
||||||
DEFINE_boolean 'bootstrap' false 'Prepare the host (packages, kernel, network)' 'i'
|
|
||||||
DEFINE_boolean 'packages' true 'Install profile packages during --bootstrap' 'k'
|
|
||||||
DEFINE_boolean 'network' true 'Configure bridges during --bootstrap' 'n'
|
|
||||||
DEFINE_string 'uplink' 'eno1' 'Physical uplink interface' 'u'
|
|
||||||
DEFINE_string 'bridge' 'br-000000' 'Main bridge, uplink is enslaved to it' 'B'
|
|
||||||
DEFINE_string 'pub_bridge' 'br-public' 'Reserved empty bridge' 'P'
|
|
||||||
DEFINE_integer 'rollback' 120 'Rollback reboot delay in seconds' 'R'
|
|
||||||
|
|
||||||
FLAGS "$@" || exit $?
|
FLAGS "$@" || exit $?
|
||||||
eval set -- "${FLAGS_ARGV}"
|
eval set -- "${FLAGS_ARGV}"
|
||||||
|
|
||||||
profile_units "${FLAGS_profile}" >/dev/null || die "profil inconnu : ${FLAGS_profile}"
|
SCRIPT_URL="${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}"
|
||||||
|
check_latest_script "${SCRIPT_URL}" "${0}" || (
|
||||||
|
[[ ${FLAGS_up_script} -eq ${FLAGS_TRUE} ]] && \
|
||||||
|
exec_with_dry_run "${FLAGS_dryrun}" "curl --silent '${SCRIPT_URL}' -o '${0}'"
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
if [[ ${FLAGS_up_script} -eq ${FLAGS_TRUE} ]]
|
download_binaries "${FLAGS_dryrun}" "${FLAGS_tag}" "${FLAGS_git_server}" "${FLAGS_repo_path}"
|
||||||
then
|
|
||||||
local SCRIPT_URL="${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}"
|
|
||||||
if ! check_latest_script "${SCRIPT_URL}" "${0}"
|
|
||||||
then
|
|
||||||
run "${FLAGS_dryrun}" "curl --silent '${SCRIPT_URL}' -o '${0}'"
|
|
||||||
die "script local différent de la branche ${FLAGS_branch} — mis à jour, relancer"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ ${FLAGS_bootstrap} -eq ${FLAGS_TRUE} ]] && \
|
|
||||||
run_bootstrap "${FLAGS_dryrun}" "${FLAGS_profile}" "${FLAGS_git_server}" "${FLAGS_repo_path}" "${FLAGS_branch}"
|
|
||||||
|
|
||||||
local TAG BIN_PATH UNIT_PATH LN_PATH ASSETS
|
|
||||||
|
|
||||||
TAG=$(resolve_tag "${FLAGS_tag}" "${FLAGS_git_server}" "${FLAGS_repo_path}")
|
|
||||||
[[ -n "${TAG}" && "${TAG}" != "null" ]] || die "impossible de déterminer la release à déployer"
|
|
||||||
|
|
||||||
BIN_PATH="/opt/two/${TAG}/bin/"
|
|
||||||
UNIT_PATH="/opt/two/${TAG}/units/"
|
|
||||||
LN_PATH="/opt/two/bin/"
|
|
||||||
|
|
||||||
ASSETS=$(release_assets "${FLAGS_git_server}" "${FLAGS_repo_path}" "${TAG}")
|
|
||||||
[[ -n "${ASSETS}" ]] || die "aucun asset dans la release ${TAG}"
|
|
||||||
|
|
||||||
fetch_assets "${FLAGS_dryrun}" "${FLAGS_profile}" "${ASSETS}"
|
|
||||||
|
|
||||||
install_units "${FLAGS_dryrun}" "${FLAGS_profile}"
|
|
||||||
switch_binaries "${FLAGS_dryrun}" "${FLAGS_profile}" "${ASSETS}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && (main "$@" || exit 1)
|
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && (main "$@" || exit 1)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue