Blog ·
Un mot de capacité pour une vieille commande
Quand une ronde ajoute un champ protobuf, un vieil agent échoue bruyamment. Quand elle change le sens d’un drapeau, il répond de façon plausible et se trompe.
Par Provibr
Notre agent tourne à l’intérieur du réseau du client et compose vers l’extérieur, vers nous. Il se met à jour lui-même, mais pas instantanément et pas toujours, si bien qu’à un instant donné la flotte est un étalement de versions et que la plateforme doit savoir ce que chacune sait faire.
Elle le sait parce que l’agent le dit. À chaque session, l’agent envoie une liste de mots courts, un par capacité, et la plateforme refuse une commande dont le mot manque plutôt que de l’envoyer en espérant.
Cette ronde a ajouté le troisième mot de ce genre pour les conteneurs Proxmox, et c’est le premier pour lequel la question intéressante n’était pas de savoir s’il fallait l’ajouter, mais ce qui se serait passé sans lui.
Pourquoi ces mots existent
Le mécanisme protège contre une propriété précise de protobuf : un champ inconnu dans un oneof est jeté.
Quand une ronde ajoute une nouvelle commande sous la forme d’un nouveau champ, un agent antérieur à cette ronde lit le message, ne reconnaît pas le numéro de champ, le jette, et se retrouve avec un ProxmoxCommand sans commande dedans. Il dit alors exactement cela. Le message est honnête et parfaitement inutile : l’exploitant voit une plainte au sujet d’une enveloppe vide, et la cause se trouve quatre couches plus loin, dans un décalage de versions que personne n’a mentionné.
La plateforme refuse donc d’abord. Si le mot manque, la commande ne part jamais, et le refus nomme la capacité et la version de l’agent. C’est un moins bon résultat que la commande qui fonctionne, et un bien meilleur que la commande qui échoue d’une manière qui coûte un après-midi à retracer.
Dans les deux cas, c’est une défaillance bruyante. Il se trouve que cela compte.
Le troisième mot n’est pas un nouveau champ
Le changement de cette ronde, c’est qu’un conteneur peut désormais montrer son matériel. La commande qui le fait, GetHardware, n’est pas nouvelle. Tout agent, en remontant jusqu’aux premiers travaux sur le cycle de vie des machines virtuelles, connaît le message et sait y répondre.
Ce qui a changé, c’est un drapeau à côté d’elle, qui dit à quel type de ressource renvoie l’identifiant. Un agent antérieur à cette ronde reçoit ce drapeau, ne le connaît pas, et le jette, exactement comme protobuf le prescrit. Puis il fait ce qu’il a toujours fait d’une demande de matériel : il demande à Proxmox la configuration de la machine, sur le chemin QEMU.
Rien n’est vide. Rien ne se plaint. L’agent croit qu’on lui a posé une question qu’il comprend, et il répond.
Cela atterrit de deux façons, et aucune des deux n’est bruyante. Si Proxmox n’a rien à cet identifiant sous le chemin des machines virtuelles, on dit au client qu’une machine n’existe pas, au sujet d’un conteneur qui tourne sous ses yeux. Et si une configuration revient bel et bien et se retrouve confiée à l’analyseur bâti pour les machines virtuelles, la réponse n’est pas vide, elle est fausse.
La mauvaise réponse est précise et elle a l’air correcte
Les deux formats de configuration sont assez proches pour se lire et assez différents pour compter. Une machine virtuelle écrit sa première carte réseau sous la forme d’un modèle et d’une adresse MAC réunis par un signe égal. Un conteneur écrit d’abord un nom d’interface, puis la MAC dans un champ nommé à part.
L’analyseur des machines virtuelles prend le premier couple clé et valeur pour le modèle et l’adresse. Devant la ligne d’un conteneur, il signale donc une carte réseau dont le modèle est name et dont l’adresse MAC est ETH0.
C’est une ligne d’apparence plausible dans un tableau. Elle a un modèle, elle a une adresse, elle s’affiche sans avertissement. C’est la raison pour laquelle le chemin des conteneurs avait besoin de son propre analyseur, et c’est exactement la classe de défaut que personne ne signale, parce qu’il n’y a rien dedans à signaler. Quelqu’un regarde un écran, trouve la colonne du modèle bizarre, et passe à autre chose.
Une commande qui existe déjà est donc un changement plus dangereux qu’une commande nouvelle. La nouvelle échoue au niveau de l’enveloppe, en public. Celle-ci échoue au niveau du sens, en privé.
Pourquoi ne rien élargir et ajouter une ligne à la place
L’autre solution évidente est d’élargir le mot qui existe déjà. Il existe un mot disant que l’agent comprend le chemin des conteneurs, et il aurait suffi d’un caractère de travail pour lui faire signifier aussi la chose plus récente.
Cela refuserait toute commande de conteneur venue de tout agent qui ne s’est pas encore mis à jour, y compris ceux qui fonctionnent parfaitement aujourd’hui. Une flotte qui sert actuellement des conteneurs s’éteindrait pour une capacité qu’elle possède réellement, parce que nous aurions changé sous elle le sens de notre mot.
Un mot est une promesse au sujet de ce qui a été construit, et la promesse doit garder son sens aussi longtemps que quelqu’un la détient encore. Ajouter un troisième mot coûte une constante de chaque côté et refuse exactement les commandes qu’il faut refuser, et rien d’autre. Cela place aussi le raisonnement à un endroit où une personne le trouvera, c’est-à-dire à côté de la constante plutôt que dans un message de commit.
Les deux côtés sont deux constantes, une dans la source Rust et une dans le TypeScript partagé, chacune nommant l’autre. Cet appariement n’est pas élégant, et toute tentative de le rendre élégant se termine par une étape de construction entre deux langages qui doivent rester lisibles indépendamment.
Le bouchon en annonce deux sur trois, exprès
Il existe un agent bouchon qui nous permet de piloter la passerelle sans hyperviseur, et il annonce lui aussi les mots des conteneurs.
Il en annonce deux. Il dit qu’il connaît le chemin des conteneurs et il dit qu’il lit le matériel d’un conteneur, et il ne revendique délibérément pas le troisième, parce que réinstaller un conteneur est quelque chose qu’il ne sait pas faire.
Cela mérite d’être dit, parce qu’un bouchon qui revendique tout est un bouchon qui rend intestable chaque chemin de refus. Et l’inverse compte tout autant : sans le mot du matériel, la passerelle aurait refusé, à juste titre, et la vérification aurait mesuré en silence un refus au lieu d’une réponse. Un harnais de test qui ment sur ses propres capacités produit des résultats verts au sujet de la mauvaise chose.
Ce que nous noterions
- Protobuf jette les champs inconnus en silence, si bien qu’une commande nouvelle atteint un vieil agent sous la forme d’une enveloppe vide.
- Refusez avant d’envoyer. Un refus qui nomme la capacité et la version vaut mieux qu’une plainte venue de quatre couches plus bas.
- Le changement dangereux est celui qui n’ajoute aucun champ. Rien n’est jeté, donc rien ne se plaint.
- Le mode de défaillance que vous voulez est bruyant. Une réponse plausible et fausse ne vous sera jamais signalée.
- Deux formats de configuration qui se lisent tous les deux sont pires que deux qui ne se lisent pas.
- N’élargissez jamais un mot de capacité existant. C’est une promesse détenue par des agents que vous ne pouvez pas joindre.
- Un mot de plus coûte une constante de chaque côté, et ne refuse que ce qu’il faut refuser.
- Placez le raisonnement à côté de la constante, là où la prochaine personne se tiendra vraiment.
- Un bouchon doit décliner ce qu’il ne sait pas faire, sinon tout chemin de refus devient intestable.
- Un harnais qui en revendique trop produit des résultats verts au sujet de la mauvaise chose.
Pour être clair : un troisième mot de capacité pour les conteneurs Proxmox, ajouté à côté d’un changement qui n’a introduit aucun nouveau champ de protocole, avec le raisonnement consigné contre les deux constantes et l’agent bouchon qui en annonce deux sur trois ; la ronde a fait passer le module de tests des conteneurs de trente-cinq tests à trente-neuf, le formatage et la cible de publication sans reproche, et s’est terminée avec les paquets partagés à 5347 verts, la passerelle à 1467, les outils bouchons à 489 et la suite web à 4593, sans aucun échec, plus cinquante vérifications navigateur vertes dans deux langues. Non prouvé : rien ici n’a tourné contre un vrai Proxmox, le comportement d’un agent plus ancien sur un identifiant de conteneur est donc établi d’après les deux formats de configuration et l’analyseur plutôt qu’observé, et la chaîne en fonctionnement a été exercée contre notre propre bouchon.
Issu du travail de plateforme sur Provibr. Ce qui se passe quand deux rondes visent le même numéro de champ est dans Deux chantiers, un seul numéro de champ, la ronde qui a appris les conteneurs à l’agent pour la première fois est dans Réinstaller un conteneur, c’est le remplacer, et la façon dont un agent décide s’il peut faire confiance à une nouvelle version est dans Quand une mise à jour signée se refuse.