Aller au contenu

Blog ·

Un fil que l’on tire, et exactement un déclencheur

Tirer un fil est la partie facile. Le difficile : ce qu’un bouton de test peut toucher, qui agit quand personne ne regarde, et la durée d’une pause.

Par Provibr

Nos flux d’automatisation ont commencé comme une liste d’étapes, sont devenus un graphe, puis un canevas, et cette manche leur a enfin donné le geste que tout le monde imagine en entendant « éditeur de flux » : vous attrapez le point à droite d’un nœud et vous tirez un fil jusqu’au point à gauche d’un autre.

Ce geste tient en une poignée de lignes de code et un long commentaire qui explique pourquoi ces lignes sont là où elles sont. Le reste de la manche a été fait de questions qui n’ont rien à voir avec le glissement. Combien d’endroits peuvent démarrer un flux. Ce qu’un bouton de test a le droit de toucher. Qui agit quand personne ne regarde. Et combien de temps une étape peut faire une pause.

La capture qui mange le clic

Le canevas a une seule couche de transformation. Les nœuds y sont placés en absolu et les fils forment un SVG dans exactement la même couche, il n’y a donc pas de second système de coordonnées susceptible de se décaler du premier. Cette partie est ennuyeuse à dessein.

L’intéressant, c’est la capture du pointeur. Dès que vous appuyez sur un point de sortie, quelque chose doit suivre le pointeur jusqu’au relâchement, y compris hors de la fenêtre. La mise en œuvre évidente capture sur le point appuyé, et elle est fausse d’une manière qui ne produit aucune erreur.

La capture redirige chaque événement de pointeur suivant vers l’élément capturant, pointerup compris, et le navigateur déclenche ensuite click sur l’ancêtre commun de l’appui et du relâchement. Si le point capture lui-même, un glissement qui se termine sur un autre point n’y arrive jamais ; il revient là où il a commencé. C’est donc la surface qui capture, et l’endroit où le doigt s’est réellement levé est une question distincte, à laquelle répond document.elementFromPoint().

Cette réponse a trois étages. Un point d’entrée sous le curseur. Sinon, une boîte de nœud sous le curseur, qui mène à sa première entrée. Sinon, un aimant : l’entrée la plus proche dans un rayon de seize pixels d’écran, et c’est ce qui rend le geste indulgent sans obliger qui que ce soit à viser une cible de dix pixels.

Deux trouvailles plus modestes, qui n’apparaissent qu’une fois qu’on le construit. React attache wheel de façon passive à la racine, empêcher l’action par défaut dans son gestionnaire ne fait donc rien et la page défile pendant que vous essayez de zoomer ; cet écouteur doit être posé sur l’élément lui-même, explicitement non passif. Et le milieu d’une courbe de Bézier n’est pas le centre de sa boîte englobante : atteindre un fil consiste donc à demander au tracé un point à une longueur donnée et à le faire passer par la matrice de transformation courante.

Clic puis clic fonctionne toujours. Ce n’est pas la voie mineure : c’est celle qui fonctionne au clavier, et c’est pourquoi les points sont restés de vrais boutons.

Un déclencheur, et quatre choses qui y pendent

Un graphe a exactement un nœud déclencheur. Zéro est une erreur à l’enregistrement et deux aussi. Cela ressemble à du rangement. C’est porteur, et quatre fonctions reposent dessus.

Ce que cela apportePourquoi un seul déclencheur est nécessaire
L’atteignabilitéTout nœud exécutable doit être atteignable depuis un unique point de départ connu
La détection de cyclesUn parcours en profondeur avec marquage par couleurs exige une racine ; deux racines font deux parcours et deux réponses à fusionner
Le sélecteur de variablesLes variables qui existent dépendent de l’événement déclenché, et un second déclencheur ferait deux réponses dans un même éditeur
L’élagage du contexteUn groupe de variables que cet événement ne peut pas fournir est supprimé : le lire est donc une erreur et non une chaîne vide

Cette dernière ligne se rembourse toute seule. Il y a quatre événements déclencheurs : un serveur mis à disposition, réinstallé, supprimé, ou démarré à la main. Un serveur supprimé n’a pas d’identifiant de système d’exploitation, tout le groupe du système d’exploitation disparaît donc du contexte au lieu d’être mis à l’objet vide. Avec des variables strictes, le lire produit alors une erreur lisible « variable inconnue » au moment de l’enregistrement. Videz-le à la place et la condition qui le lit devient fausse, en silence, pour toujours, sur la branche qui comptait.

C’est aussi pourquoi il n’existe pas de nœud d’injection distinct. Le démarrage manuel est un événement déclencheur et non une seconde entrée ; en ajouter un briserait l’invariant dont dépendent les quatre fonctions, en échange d’un nœud qui se dessine un peu mieux.

Un essai ne touche rien, et parcourt quand même

L’éditeur a un bouton Test run, et un graphe peut désormais contenir un nœud qui démarre, arrête, éteint, suspend ou reprend le serveur de cette exécution. Ces deux faits sur un même écran sont un piège : quelqu’un appuiera sur tester pour voir quelle branche prend un graphe, et éteindra une machine de client.

Un essai n’envoie donc jamais de commande. Le nœud d’action revient aussitôt, marqué comme ignoré, avec un code de motif rendu dans la langue du lecteur. Et surtout : il prend la branche ok. Un essai qui tomberait sur la branche d’erreur affirmerait que le graphe fait quelque chose qu’il ne fait pas, ce qui est pire que de ne rien dire. Agir pour de vrai passe par l’injection, qui est une exécution réelle et qui demande une confirmation.

Qui agit quand personne ne regarde

Une exécution de flux n’a pas de spectateur. Sur une commande, la personne qui a cliqué sur Commander n’est au mieux qu’accidentellement celle à qui l’action devrait être attribuée. Sur une suppression, elle est peut-être déjà partie. Mais trouver un serveur exige un acteur doté d’un profil de droits, et ce profil doit venir de quelque part.

Le choix est qu’une exécution agit comme la personne propriétaire du compte, pour trois raisons par ordre de poids. Le moment de l’autorisation est le déploiement et non l’exécution : enregistrer un flux exige le module de scripts, l’accrocher à une offre exige des droits sur les offres, et c’est la même forme qu’une tâche planifiée ou que le travailleur de supervision. Agir comme celui qui a cliqué rendrait le graphe non reproductible : le même flux démarrerait une machine pour un acheteur et pas pour un autre, à cause d’une case de droits qui n’a rien à voir avec le flux. Et la vraie limite reste dure de toute façon : la recherche reçoit l’identifiant d’organisation de l’exécution et refuse toute ligne en dehors, et ce nœud ne peut par construction adresser que le serveur de cette exécution.

Avant qu’une commande n’existe, deux tamis doivent tous deux dire oui : ce fournisseur sait-il faire cette commande, et cette sorte de ressource le peut-elle. Sur Proxmox, aucune des cinq actions n’est aujourd’hui refusée sur un conteneur, et c’est précisément pourquoi l’essai de bout en bout demande aussi un agrandissement de disque et exige que celui-là soit refusé. Un tamis qui ne dit jamais non est indiscernable d’un tamis qui n’est pas branché.

Cinq minutes, parce que la pause est dans une requête

Le nœud de temporisation accepte de cinq à trois cents secondes. La consigne demandait six cents, et six cents est faux pour une raison mesurable : un flux déclenché par une commande est attendu dans la requête de commande. Dix minutes de pause, ce sont dix minutes pendant lesquelles un client payant regarde un indicateur de chargement.

Il n’existe pas d’état d’attente durable, et tant qu’il n’en existe pas, une pause ne peut pas survivre à une requête. Cette couture porte un nom dans le code pour que « pourquoi mon flux ne peut-il pas attendre une heure » soit une question dont la réponse se trouve. Elle exige une file capable de conserver une exécution à moitié terminée et de la reprendre plus tard, et ce n’est qu’une fois celle-ci en place que les boucles pourront être envisagées.

Le nœud HTTP, et pourquoi ce n’est pas fetch

Un nœud HTTP dans un outil d’automatisation est une machine à falsifier des requêtes côté serveur si l’on n’y prend pas garde, et y prendre garde exclut ici le client standard.

fetch ne sait pas faire la seule chose que cette politique exige : se connecter à une adresse que nous avons choisie pendant que le nom d’hôte reste dans Host et dans SNI. Il résout lui-même, et cette seconde résolution est exactement la fenêtre où vit le réassociement DNS. Vous tamisez une adresse publique, et une demi-seconde plus tard le client se connecte à la boucle locale parce que la durée de vie de l’enregistrement valait zéro.

Le client HTTP de Node accepte une résolution maison, et la nôtre renvoie exactement une adresse : celle que le tamis a approuvée. Host et SNI restent dérivés du nom d’hôte, TLS est donc validé contre le nom. La validation du certificat est active sans interrupteur pour l’éteindre, et les variables d’environnement de mandataire ne font rien, parce que ce client ne les lit pas.

Les redirections sont suivies à la main, trois au maximum, et chaque saut est retamisé depuis le début : schéma, information d’utilisateur, port, DNS, chaque adresse. Une redirection ouverte vers un point de métadonnées d’un nuage est refusée au deuxième saut, même si le premier était un hôte public respectable. Sept noms d’en-tête sont autorisés, et deux d’entre eux ne peuvent porter qu’une référence à un secret stocké, déchiffré pour la requête, caviardé dans tout ce qui est conservé, et consigné par son seul nom.

Ce que nous noterions

  1. Capturez sur l’enveloppe, pas sur ce que vous avez saisi. La capture du pointeur redirige tout ce qui suit, et le symptôme est un geste qui se termine en silence là où il a commencé.
  2. Un geste indulgent demande une chaîne de replis, pas une cible plus grande. Élément sous le curseur, puis son parent, puis un seuil de distance.
  3. Une seule entrée achète quatre fonctions. Atteignabilité, détection de cycles, sélecteur de variables et élagage du contexte exigent tous les quatre une racine connue. Comptez ce que vous perdez avant d’en ajouter une seconde.
  4. Supprimez ce qui ne peut pas exister ; ne le videz pas. Un groupe de variables absent échoue bruyamment à l’enregistrement ; un groupe vide échoue en silence à l’exécution.
  5. Un bouton de test capable de toucher la production n’est pas un bouton de test. Ignorer tout en prenant la branche de succès est la combinaison honnête.
  6. Une pause est bornée par ce qui l’attend. Si l’appelant est une requête HTTP, la pause se compte en secondes.

Pour être clair sur ce dont il s’agit : les flux sont accessibles depuis l’écran des scripts et le canevas est désormais le seul éditeur. Trois choses ne sont pas prouvées. L’attente d’une commande réellement terminée n’a pas été mesurée contre un vrai hyperviseur, le glissement a été piloté par des événements simulés du protocole du navigateur et non par une main, et rien de tout cela n’a été mesuré sur une compilation de production.

Issu du travail de plateforme de Provibr. Pourquoi le graphe doit d’abord être fini est expliqué dans un flux est un graphe, et un graphe doit être fini, et comment un écran de synthèse lit les tables des autres sans jamais y écrire est expliqué dans projeter, jamais remplacer.

Commencez avec un hôte et un agent

Créez un compte, installez l’agent avec une seule commande et connectez votre premier hôte Proxmox. La période d’essai dure 14 jours et ne demande aucune coordonnée de paiement.