Ir al contenido

Blog ·

Un cable que se arrastra, y un solo disparador

Arrastrar un cable es la parte fácil. Lo difícil era qué puede tocar un botón de prueba, quién actúa cuando nadie mira y cuánto puede durar una pausa.

Por Provibr

Nuestros flujos de automatización empezaron como una lista de pasos, se convirtieron en un grafo, luego en un lienzo, y en esta ronda recibieron por fin el gesto que todo el mundo imagina al oír «editor de flujos»: se agarra el punto de la derecha de un nodo y se tira de un cable hasta el punto de la izquierda de otro.

Ese gesto son un puñado de líneas de código y un comentario largo que explica por qué esas líneas están donde están. El resto de la ronda fueron preguntas que nada tienen que ver con arrastrar. Cuántos sitios pueden arrancar un flujo. Qué puede tocar un botón de prueba. Quién actúa cuando nadie mira. Y cuánto puede pausar un paso.

La captura que se come el clic

El lienzo tiene una sola capa de transformación. Los nodos están colocados de forma absoluta dentro de ella y los cables son un SVG en esa misma capa, así que no hay un segundo sistema de coordenadas que pueda desfasarse del primero. Esa parte es aburrida a propósito.

Lo interesante es la captura del puntero. Al pulsar sobre un punto de salida, algo tiene que seguir al puntero hasta que se suelta, incluso fuera de la ventana. La implementación evidente captura sobre el punto pulsado, y es incorrecta de una forma que no produce ningún error.

La captura redirige todos los eventos de puntero posteriores al elemento que captura, pointerup incluido, y el navegador dispara después click sobre el ancestro común de la pulsación y la soltada. Si captura el propio punto, un arrastre que termina en otro punto nunca llega allí; vuelve a donde empezó. Por eso captura la superficie, y dónde se soltó realmente el dedo es una pregunta aparte, que responde document.elementFromPoint().

Esa respuesta tiene tres escalones. Un punto de entrada bajo el cursor. Si no, una caja de nodo bajo el cursor, que lleva a su primera entrada. Si no, un imán: la entrada más cercana dentro de dieciséis píxeles de pantalla, que es lo que vuelve indulgente al gesto sin obligar a nadie a acertar en un blanco de diez píxeles.

Dos hallazgos menores que solo aparecen al construirlo. React engancha wheel de forma pasiva en la raíz, así que impedir la acción por omisión dentro de su manejador no hace nada y la página se desplaza mientras se intenta ampliar; ese oyente tiene que ir sobre el elemento mismo, explícitamente no pasivo. Y el punto medio de una curva de Bézier no es el centro de su caja envolvente, así que acertar en un cable significa pedirle al trazado un punto a cierta longitud y pasarlo por la matriz de transformación actual.

Clic y luego clic sigue funcionando. No es el camino menor: es el que funciona con teclado, y por eso los puntos siguieron siendo botones de verdad.

Un disparador, y cuatro cosas colgando de él

Un grafo tiene exactamente un nodo disparador. Cero es un error al guardar y dos es un error al guardar. Suena a pulcritud. Es portante, y cuatro funciones descansan en ello.

Qué aportaPor qué hace falta un solo disparador
AlcanzabilidadTodo nodo ejecutable debe ser alcanzable desde un único punto de partida conocido
Detección de ciclosUna búsqueda en profundidad con marcado por colores necesita una raíz; dos raíces son dos búsquedas y dos respuestas que fusionar
El selector de variablesQué variables existen depende del suceso que disparó, y un segundo disparador serían dos respuestas en un mismo editor
La poda del contextoUn grupo de variables que este suceso no puede aportar se elimina, así que leerlo es un error y no una cadena vacía

Esa última fila se paga sola. Hay cuatro sucesos disparadores: un servidor aprovisionado, reinstalado, eliminado o arrancado a mano. Un servidor eliminado no tiene identificador de sistema operativo, así que el grupo entero del sistema operativo desaparece del contexto en lugar de quedar en un objeto vacío. Con variables estrictas, leerlo produce entonces un error legible de «variable desconocida» en el momento de guardar. Si en cambio se deja vacío, la condición que lo lee resulta falsa, en silencio, para siempre, en la rama que importaba.

Por eso tampoco hay un nodo de inyección aparte. Arrancar a mano es un suceso disparador y no una segunda entrada; añadir uno rompería la invariante de la que dependen las cuatro funciones, a cambio de un nodo que dibuja algo mejor.

Una ejecución de prueba no toca nada, y aun así recorre

El editor tiene un botón Test run, y un grafo ya puede contener un nodo que arranca, detiene, apaga, suspende o reanuda el servidor de esa ejecución. Esos dos hechos en una misma pantalla son una trampa: alguien pulsará probar para ver qué rama toma un grafo y apagará una máquina de un cliente.

Por eso una ejecución de prueba nunca envía una orden. El nodo de acción vuelve de inmediato, marcado como omitido, con un código de motivo mostrado en el idioma de quien lee. Y lo esencial: toma la rama ok. Una prueba que cayera a la rama de error afirmaría que el grafo hace algo que no hace, y eso es peor que no decir nada. Actuar de verdad pasa por la inyección, que es una ejecución real y pide confirmación.

Quién actúa cuando nadie mira

Una ejecución de flujo no tiene espectador. En un pedido, quien pulsó Pedir es como mucho por casualidad la persona a quien atribuir la acción. En una eliminación puede que ya no esté. Pero encontrar un servidor exige un actor con un perfil de permisos, y ese perfil tiene que salir de algún sitio.

La decisión es que una ejecución actúa como la persona propietaria de la cuenta, por tres motivos en orden de peso. El momento de autorización es el despliegue y no la ejecución: guardar un flujo exige el módulo de scripts, colgarlo de un plan exige permisos de planes, y esa es la misma forma que un cron o el trabajador de supervisión. Actuar como quien pulsó volvería el grafo irreproducible: el mismo flujo arrancaría una máquina para un comprador y no para otro, por una casilla de permisos que nada tiene que ver con el flujo. Y el límite real sigue siendo duro igualmente: la búsqueda recibe el identificador de organización de la ejecución y rechaza toda fila fuera de él, y este nodo solo puede dirigirse, por construcción, al servidor de esta ejecución.

Antes de crear cualquier orden, dos cribas tienen que decir que sí las dos: puede este proveedor con esta orden, y puede esta clase de recurso con ella. Hoy en Proxmox ninguna de las cinco acciones se rechaza en un contenedor, y justo por eso la prueba de extremo a extremo pide también una ampliación de disco y exige que esa se rechace. Una criba que nunca dice que no es indistinguible de una criba que no está conectada.

Cinco minutos, porque la pausa está dentro de una petición

El nodo de retardo admite de cinco a trescientos segundos. El encargo pedía seiscientos, y seiscientos es incorrecto por un motivo medible: un flujo disparado por un pedido se espera dentro de la petición del pedido. Diez minutos de pausa son diez minutos de un cliente que paga mirando un indicador de carga.

No hay un estado de espera duradero, y mientras no lo haya una pausa no puede sobrevivir a una petición. Esa costura lleva un nombre en el código para que «por qué mi flujo no puede esperar una hora» sea una pregunta con respuesta localizable. Exige una cola capaz de guardar una ejecución a medias y reanudarla más tarde, y solo cuando exista cabrá plantearse los bucles.

El nodo HTTP, y por qué no es fetch

Un nodo HTTP en una herramienta de automatización es una máquina de falsificación de peticiones del lado del servidor si no se tiene cuidado, y tenerlo descarta aquí el cliente estándar.

fetch no puede hacer lo único que esta política exige: conectarse a una dirección elegida por nosotros mientras el nombre de máquina sigue en Host y en SNI. Resuelve por su cuenta, y esa segunda resolución es justo la ventana en la que vive la reasignación de DNS. Se criba una dirección pública y medio segundo después el cliente se conecta al bucle local porque la vigencia del registro era cero.

El cliente HTTP propio de Node acepta una resolución propia, y la nuestra devuelve exactamente una dirección: la que aprobó la criba. Host y SNI se siguen derivando del nombre de máquina, así que TLS se valida contra el nombre. La validación de certificado está activa sin interruptor para apagarla, y las variables de entorno de proxy no hacen nada, porque ese cliente no las lee.

Las redirecciones se siguen a mano, tres como mucho, y cada salto se criba de nuevo desde cero: esquema, información de usuario, puerto, DNS, cada dirección. Una redirección abierta hacia un punto de metadatos de una nube se rechaza en el segundo salto aunque el primero fuera una máquina pública respetable. Se admiten siete nombres de cabecera, y dos de ellos solo pueden llevar una referencia a un secreto almacenado, que se descifra para la petición, se censura en todo lo que se guarda y se registra solo por su nombre.

Lo que anotaríamos

  1. Capture sobre el contenedor, no sobre lo que agarró. La captura del puntero redirige todo lo que viene después, y el síntoma es un gesto que termina en silencio donde empezó.
  2. Un gesto indulgente necesita una cadena de reservas, no un blanco mayor. Elemento bajo el cursor, luego su padre, luego un umbral de distancia.
  3. Una sola entrada compra cuatro funciones. Alcanzabilidad, detección de ciclos, selector de variables y poda del contexto necesitan las cuatro una raíz conocida. Cuente lo que pierde antes de añadir una segunda.
  4. Elimine lo que no puede existir; no lo vacíe. Un grupo de variables ausente falla en voz alta al guardar; uno vacío falla en silencio durante la ejecución.
  5. Un botón de prueba capaz de tocar producción no es un botón de prueba. Omitir y aun así tomar la rama de éxito es la combinación honesta.
  6. Una pausa la limita quien la espera. Si quien llama es una petición HTTP, la pausa va en segundos.

Para que quede claro qué es esto: los flujos son accesibles desde la pantalla de scripts y el lienzo es ya el único editor. Tres cosas no están probadas. La espera de una orden realmente terminada no se midió contra un hipervisor real, el arrastre se accionó con sucesos simulados del protocolo del navegador y no con una mano, y nada de ello se midió sobre una compilación de producción.

Del trabajo de plataforma de Provibr. Por qué el grafo tiene que ser finito de entrada está en un flujo es un grafo, y un grafo debe ser finito, y cómo una pantalla de resumen lee tablas ajenas sin escribir jamás en ellas está en proyectar, nunca sustituir.

Empiece con un host y un agente

Cree una cuenta, instale el agente con un solo comando y conecte su primer host de Proxmox. El periodo de prueba dura 14 días y no pide ningún dato de pago.