Moteur de restauration¶
Un plan est une liste d'étapes, exécutées dans l'ordre, sur le runner. La première qui échoue arrête l'exécution : les suivantes ne sont pas jouées, et les sondes non plus.
Il existe neuf types d'étapes, et il n'y en a pas d'autres.
| Type | Ce qu'il fait |
|---|---|
fetch |
Rapatrie la sauvegarde dans l'espace de travail. |
unpack |
Décompresse une archive. |
start_sandbox |
Démarre un conteneur (base de données, service). |
wait |
Attend qu'un port, une URL ou une commande réponde. |
exec_in_sandbox |
Exécute une commande dans le conteneur. |
restore_postgres |
Rejoue un dump PostgreSQL. |
restore_mysql |
Rejoue un dump MySQL / MariaDB. |
restore_mongo |
Rejoue un dump MongoDB. |
restore_ldap |
Recharge un LDIF. |
L'enchaînement habituel est fetch → unpack → start_sandbox → wait →
restore_*, puis les sondes.
Comment un fichier est retrouvé¶
Plusieurs étapes prennent un champ input. Sa résolution est la même partout, et
elle est plus tolérante qu'un simple nom de fichier.
- Si
inputne contient pas de joker et que le fichier existe à la racine de l'espace de travail, c'est celui-là. - Sinon,
inputest traité comme un motif (*.sql,dump-*.tar.gz). - Si rien ne correspond à la racine, la recherche descend dans les sous-répertoires : le nom est comparé au nom de chaque fichier de l'arborescence.
- Si plusieurs fichiers correspondent, le plus gros gagne — une sauvegarde arrive rarement seule, à côté d'une somme de contrôle et d'un manifeste.
C'est ce qui fait qu'une archive construite normalement — tar czf
backup.tar.gz backup/, qui se décompresse en backup/dump.sql et non
dump.sql — fonctionne avec input: "dump.sql", sans que vous ayez à écrire la
disposition interne de l'archive dans le plan.
Un dump PostgreSQL au format répertoire est un répertoire : il bénéficie de la même tolérance. Les fichiers l'emportent quand les deux existent.
Cette résolution ne s'applique pas à exec_in_sandbox
Le champ cmd de exec_in_sandbox est exécuté sans shell. Un
/workspace/*/schema.ldif y reste un caractère * littéral, il n'est jamais
développé. Voir exec_in_sandbox.
Les secrets¶
Partout où une valeur peut être un secret — clé S3, mot de passe de base, variable d'environnement d'un conteneur — vous écrivez une référence, jamais la valeur :
Le runner la résout localement, à partir de son propre fichier de configuration
(env://, file://, vault://, sops://). Le Control Plane ne voit que la
référence.
fetch¶
| Champ | Rôle |
|---|---|
source |
s3 ou minio ; http ou https ; local, file ou nfs. |
output |
Nom du fichier dans l'espace de travail. |
max_age |
Âge maximal de la sauvegarde à la source, par exemple 26h. |
S3 / MinIO
| Champ | Rôle |
|---|---|
bucket |
Le bucket. |
key |
La clé exacte de l'objet. |
prefix |
Le préfixe / répertoire dans lequel chercher. |
endpoint |
Le point d'accès (pour MinIO ou un S3 compatible). |
region |
La région. |
access_key, secret_key |
Références secret://. |
selection_strategy |
latest_modified ou latest_name, pour prendre la dernière sauvegarde d'un préfixe au lieu d'une clé fixe. |
file_pattern |
Motif filtrant les objets du préfixe, par exemple *.sql.gz. |
HTTP / HTTPS
| Champ | Rôle |
|---|---|
url |
L'URL à télécharger. |
headers |
En-têtes supplémentaires. |
insecure |
true pour ne pas vérifier le certificat. |
Local / NFS
| Champ | Rôle |
|---|---|
path |
Le chemin sur la machine du runner. |
max_age¶
C'est la seule chose qu'un test de restauration ne peut pas déduire des données elles-mêmes. Une tâche de sauvegarde arrêtée depuis trois mois produit un fichier qui se restaure parfaitement.
max_age fait échouer l'étape quand l'objet à la source est plus vieux que la
durée donnée. La date utilisée est celle de la source, pas celle de la copie.
Si la source ne sait pas dire quand l'objet a été écrit, l'étape échoue aussi :
une garantie invérifiable n'est pas une garantie.
Ce qui entre dans le rapport¶
Après un fetch réussi, le runner identifie le fichier qui vient d'apparaître,
calcule son SHA-256 et l'inscrit dans le rapport signé : nom, source, bucket,
préfixe, URL, taille, empreinte, date de modification à la source. Un mot de
passe présent dans l'URL est retiré avant.
Si plusieurs fichiers sont apparus d'un coup, rien n'est inscrit : une identité fausse dans une preuve est pire qu'une identité absente.
unpack¶
| Champ | Rôle |
|---|---|
input |
L'archive. Vide : le plus gros fichier de l'espace de travail est pris. |
format |
Vide ou auto : déduit de l'extension. |
output |
Répertoire de destination dans l'espace de travail. |
Formats acceptés : tar, tar.gz, tgz, tar.bz2, tbz2, tar.xz,
txz, gzip, gz, zip.
Détection automatique, par suffixe : .tar.gz et .tgz → tar.gz ;
.tar.bz2 et .tbz2 → tar.bz2 ; .tar.xz et .txz → tar.xz ; .tar →
tar ; .gz → gzip ; .zip → zip. Un nom qui ne finit par aucun de ces
suffixes fait échouer l'étape.
start_sandbox¶
Démarre le conteneur dans lequel les données seront restaurées.
| Champ | Rôle |
|---|---|
image |
L'image Docker. |
name |
Le nom du conteneur. C'est le nom d'hôte que les sondes utiliseront. |
env |
Variables d'environnement. Une valeur peut être une référence secret://. |
ports |
Ports publiés. |
volumes |
Volumes montés. |
command |
Commande de l'image, si vous la remplacez. |
healthcheck |
Voir ci-dessous. |
Le runner place le bac à sable et les sondes sur un réseau propre à l'exécution :
c'est ce qui permet à une sonde de joindre le conteneur par son name.
healthcheck¶
| Champ | Rôle |
|---|---|
cmd |
La commande, sous forme de liste. |
interval |
Entre deux essais, par exemple "5s". |
timeout |
Délai d'un essai, celui de Docker. |
retries |
Nombre d'essais. |
Quand un healthcheck est présent, l'étape attend que le conteneur soit sain.
L'attente totale vaut interval × retries ; si l'un des deux manque, elle vaut
60 secondes.
Le conteneur et ses volumes sont détruits à la fin de l'exécution, quel qu'en soit le résultat.
wait¶
Attend, depuis le runner, qu'une condition soit remplie.
| Champ | Rôle |
|---|---|
type |
tcp, http ou command. |
host, port |
Pour tcp. |
url |
Pour http. Prêt sur un code 2xx. |
command |
Pour command. Prêt quand la commande sort en 0. |
timeout |
Attente totale. Défaut : 60 s. |
interval |
Entre deux essais. Défaut : 2 s. |
timeout et interval s'écrivent en durée Go : "90s", "2m".
Un essai tcp est plafonné à 3 secondes, un essai http à 5 secondes ; c'est
timeout qui borne l'ensemble.
La commande de wait s'exécute sur le runner, à travers sh -c, avec
l'espace de travail comme répertoire courant. Pour exécuter quelque chose dans le
conteneur, c'est exec_in_sandbox.
exec_in_sandbox¶
L'échappatoire pour la préparation qu'aucune autre étape ne couvre :
start_sandbox ne fait que démarrer le conteneur, et certaines images ont besoin
d'une commande à l'intérieur avant d'être utilisables — 389-ds, par exemple, ne
crée pas le backend du suffixe qu'on lui demande de servir.
| Champ | Rôle |
|---|---|
cmd |
La commande et ses arguments. Obligatoire. |
env |
Variables d'environnement de la commande. Les valeurs secret:// sont résolues. |
ignore_failure |
true pour qu'un code de retour non nul ne soit pas fatal. Défaut : false. |
La commande est exécutée dans le dernier conteneur démarré. Sans
start_sandbox avant, l'étape échoue.
cmd : sans shell, jamais¶
C'est le point sur lequel les plans se cassent le plus souvent.
cmd est exécuté directement dans le conteneur, pas à travers un shell. Il
n'y a donc :
- aucun développement de joker —
/workspace/*/schema.ldifreste littéral ; - aucune substitution de variable —
$HOMEreste$HOME; - aucune redirection ni tube —
>,|,&&sont des arguments comme les autres.
Deux écritures sont acceptées :
La forme chaîne est découpée aux espaces, mais un fragment entre guillemets
simples ou doubles reste un seul argument — un suffixe LDAP comme
"dc=example, dc=org" contient des espaces et des virgules et serait déchiré par
une découpe naïve. Rien d'autre du comportement d'un shell n'est implémenté.
Si vous avez vraiment besoin d'un shell, appelez-le explicitement :
La sortie de la commande est conservée dans le résultat de l'étape, tronquée à 32 Kio.
Les étapes de restauration¶
Les quatre étapes restore_* partagent la même configuration et le même
principe : le runner exécute l'outil de restauration à l'intérieur du bac à
sable, sur le fichier de l'espace de travail. Elles exigent toutes qu'un
start_sandbox les précède.
| Champ | Rôle |
|---|---|
input |
Le dump. Résolu comme décrit plus haut. |
host |
Défaut 127.0.0.1. localhost est réécrit en 127.0.0.1. |
port |
Défaut selon le moteur. |
user |
L'utilisateur. |
password |
Le mot de passe, ou une référence secret://. |
database |
La base cible. |
format |
Vide : détecté à partir du fichier. |
options |
Options supplémentaires passées à l'outil. |
stop_on_error |
Voir ci-dessous. |
stop_on_error¶
Non renseigné vaut true.
psql et pg_restore continuent après une erreur SQL et sortent en 0 : un dump
à moitié appliqué serait rapporté comme une restauration réussie. C'est exactement
la fausse preuve que ce produit existe pour empêcher.
Ne le mettez à false que pour un dump qui référence sciemment des rôles, des
extensions ou des tablespaces absents du bac à sable.
restore_postgres¶
Port par défaut 5432, utilisateur par défaut postgres, base par défaut restoreproof.
Formats détectés : plain, custom, directory, tar.
restore_mysql¶
Port par défaut 3306, utilisateur par défaut root, base par défaut restoreproof.
Formats détectés : sql, mysqlpump, shell, xtraback. Un dump
--all-databases est reconnu et la base cible est alors laissée vide, puisque le
dump porte ses propres CREATE DATABASE.
restore_mongo¶
Port par défaut 27017. Formats détectés : archive (un fichier, avec ou sans
--gzip) et directory (un répertoire de .bson).
| Champ supplémentaire | Rôle |
|---|---|
auth_database |
Où MongoDB vérifie les identifiants. Défaut admin, là où l'image officielle place le compte créé par MONGO_INITDB_ROOT_*. |
restore_ldap¶
Port par défaut 389. Le LDIF est rejoué en ligne avec ldapadd, pas avec
slapadd.
| Champ supplémentaire | Rôle |
|---|---|
bind_dn |
Le DN avec lequel ldapadd s'authentifie, typiquement le rootdn (cn=admin,dc=example,dc=com). C'est le vocabulaire LDAP : user et database n'ont pas d'équivalent ici. |
base_dn |
Le suffixe racine de l'annuaire. Il n'est pas passé à ldapadd, qui prend les DN dans le LDIF ; il est enregistré pour que le rapport puisse dire quel sous-arbre l'exécution a exercé. |
Un export slapcat ou db2ldif contient des attributs opérationnels que le
serveur cible régénère lui-même : entryUUID, entryCSN, creatorsName,
createTimestamp, structuralObjectClass, memberOf… ldapadd les refuse,
parce qu'ils appartiennent au serveur. Le runner écrit donc à côté de l'original
une copie nettoyée, nommée <base>.rp-load — sans l'extension .ldif, pour
qu'un input: "*.ldif" ne puisse jamais la reprendre pour la sauvegarde. Le
fichier d'origine n'est jamais modifié.
Le champ format accepte ici auto (défaut : le mode est déduit de la présence
de ces attributs), sanitize (nettoyer, toujours) et plain (rejouer tel quel,
pour un LDIF écrit à la main ou exporté par une application).
Ce que cette restauration prouve, et ce qu'elle ne prouve pas
Les entrées nettoyées sont rechargées avec de nouveaux entryUUID,
entryCSN et horodatages, produits par le serveur du bac à sable. Ce qui est
prouvé, c'est que les données et les mots de passe de l'annuaire reviennent
et sont utilisables par le protocole standard — pas que la base interne du
serveur est identique octet pour octet à celle qui a été sauvegardée.
Le schéma est vérifié avant d'écrire la première entrée. Le runner compare
les types d'attributs et les classes d'objets utilisés par le dump à ceux que
l'annuaire du bac à sable déclare dans son sous-schéma. S'il en manque, l'étape
échoue en donnant la liste complète, au lieu du Undefined attribute type
(17) que ldapadd rend une erreur à la fois. Voir
le dépannage.
Si le contrôle ne peut pas s'exécuter — un serveur qui ne répond pas à une requête sur son root DSE — il est simplement ignoré, avec un avertissement dans les journaux : un serveur inhabituel n'est pas la preuve d'une mauvaise sauvegarde.