Trois scripts qui tournent. Ils tiennent en quelques lignes parce que l'API rend déjà les nombres sur lesquels décider : le solde, le débit, l'autonomie, le quota.
Tous supposent FFXF_TOKEN dans l'environnement et
jq installé.
Un runner éphémère
Le cas d'école : une machine née pour une tâche, détruite à la fin. Coût réel d'un travail d'une heure sur un Nano : 0,018 CAD.
#!/usr/bin/env bash
set -euo pipefail
API=https://api.ffxf.net/v1
AUTH="Authorization: Bearer $FFXF_TOKEN"
NOM="runner-$(date +%s)"
# Commander. La clé d'idempotence protège d'un double envoi sur reprise réseau.
VM=$(curl -sf "$API/vms" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: $NOM" \
-d "{\"plan\":\"nano\",\"region\":\"montreal\",\"image\":\"debian-13\",
\"hostname\":\"$NOM\",\"billing\":\"hourly\",\"password_delivery\":\"none\",
\"ssh_keys\":[\"$FFXF_SSH_FINGERPRINT\"]}" | jq -r .data.vm.id)
# Attendre la livraison : le provisionnement est asynchrone.
until [ "$(curl -sf "$API/vms/$VM" -H "$AUTH" | jq -r .data.status)" = "running" ]; do
sleep 5
done
IP=$(curl -sf "$API/vms/$VM" -H "$AUTH" | jq -r .data.ipv4)
ssh -o StrictHostKeyChecking=accept-new "debian@$IP" "./ma-tache.sh"
# Détruire. Le nom d'hôte confirme : sans lui, l'API refuse.
curl -sf -X DELETE "$API/vms/$VM?confirm=$NOM" -H "$AUTH"
Notez password_delivery: none : aucune machine sans mot de
passe n'est plus sûre qu'une machine dont le mot de passe traîne dans une
boîte mail. Elle exige au moins une clé SSH.
Éteindre avant la panne sèche
L'API dit combien de temps le solde tient au débit actuel. Un script qui lit ce nombre n'a jamais de machine coupée par surprise.
#!/usr/bin/env bash
# À passer toutes les heures. Sous 12 h d'autonomie, éteint les machines
# jetables pour que le crédit restant serve à celles qui comptent.
API=https://api.ffxf.net/v1
AUTH="Authorization: Bearer $FFXF_TOKEN"
AUTONOMIE=$(curl -sf "$API/account" -H "$AUTH" | jq -r '.data.hourly.runway_hours // 9999')
if (( $(echo "$AUTONOMIE < 12" | bc -l) )); then
curl -sf "$API/vms" -H "$AUTH" \
| jq -r '.data[] | select(.hostname | startswith("runner-")) | .id' \
| while read -r vm; do
curl -sf -X POST "$API/vms/$vm/actions" -H "$AUTH" \
-H "Content-Type: application/json" -d '{"type":"shutdown"}'
done
fi
Une machine éteinte continue d'être facturée : l'heure court tant que la
machine existe, allumée ou non. Pour arrêter le compteur, il faut détruire.
Ce script gagne du temps, il n'économise rien — remplacez
shutdown par la suppression si c'est l'économie que vous
cherchez.
Un inventaire qui coûte ce qu'il annonce
Le relevé par machine répond à la seule question qui se pose en fin de mois : laquelle coûte cher.
curl -sf "https://api.ffxf.net/v1/usage?group_by=vm" -H "Authorization: Bearer $FFXF_TOKEN" \
| jq -r '.data[] | [.hostname // "(détruite)", .hours, .amount] | @tsv' \
| sort -k3 -rn
Les machines détruites y figurent avec leur nom : ce qu'elles ont coûté ne disparaît pas avec elles.
Gérer les refus proprement
Un script qui lit le code plutôt que le message survit aux
reformulations et aux traductions.
reponse=$(curl -s -w '\n%{http_code}' "$API/vms" -H "$AUTH" \
-H "Idempotency-Key: $NOM" -H "Content-Type: application/json" -d "$CORPS")
corps=$(echo "$reponse" | head -n -1); code=$(echo "$reponse" | tail -n1)
case "$(echo "$corps" | jq -r '.error.code // "ok"')" in
ok) echo "commandée" ;;
insufficient_credit) echo "recharger $(echo "$corps" | jq -r .error.details.missing) CAD" ;;
quota_reached) echo "plafond atteint, détruire une machine ou écrire au support" ;;
rate_limited) sleep 60; exec "$0" "$@" ;;
*) echo "refus : $corps"; exit 1 ;;
esac