Salta al contenuto

Documentazione

Flussi e automazione

Un flusso è un disegno che gira. Si mettono nodi su una tela, si tirano fili tra di essi e la piattaforma percorre quel disegno ogni volta che arriva il momento scelto. Questa pagina dice cosa può fare un flusso, cosa significa ogni nodo e dove stanno esattamente i suoi limiti.

Che cos’è un flusso

Un flusso è un grafo orientato: nodi collegati da fili, percorsi a partire da un unico punto di partenza. Non è, di proposito, uno script con un ciclo. Quattro regole ne fissano la forma, e l’editor si rifiuta di pubblicare un disegno che ne rompa una, indicando il nodo sbagliato.

  • Esattamente un innesco. Il nodo di innesco è l’unica entrata, ed è lui a decidere quali variabili esistono nel resto del disegno.
  • Nessun ciclo. Un filo che riporta a un nodo da cui era uscito viene rifiutato, perché un flusso non sa attendere: un ciclo semplicemente non finirebbe mai.
  • Ogni nodo deve essere raggiungibile dall’innesco. Un nodo rimasto appeso è un errore che una schermata non mostra, quindi è un errore al momento della pubblicazione.
  • I rami vengono percorsi uno dopo l’altro, mai affiancati. Due rami contemporanei scriverebbero in state nello stesso istante, e il risultato dipenderebbe dal caso dell’orologio.
Un disegno accetta al massimo 80 nodi e 160 fili, di cui al massimo 25 che facciano davvero qualcosa. Quest’ultimo numero è lo stesso delle vecchie liste di passi; disegnare invece di elencare non lo alza.

La tela

L’editor è una tela, e non ce n’è una seconda. Un flusso nuovo si apre subito su di essa, e un flusso che era una lista dritta di passi si apre come una catena dritta di nodi.

  • La tavolozza contiene i dodici tipi di nodo. Basta un clic per posarne uno sulla tela, oppure lo si trascina nel punto desiderato.
  • Un nodo si sposta trascinandolo per la sua intestazione. Tutto il resto del nodo è un comando e quindi non avvia un trascinamento.
  • Un filo va da un punto di uscita a un punto di entrata. Si trascina dall’uno all’altro, oppure si fa clic prima sull’uscita e poi sull’entrata. Le due cose fanno lo stesso, e la seconda è la via da tastiera.
  • Lasciare un filo nel vuoto non fa nulla. Per rimuoverne uno lo si seleziona e si preme Canc, oppure si usa la croce che porta sopra.
  • Lo zoom va dal 50 al 200 per cento, Adatta mostra tutto il disegno e trascinando lo sfondo si sposta la vista. Non c’è una mappa in miniatura.
La vista a elenco è lo stesso grafo in forma di testo, con lo stesso pannello delle proprietà accanto. È un’alternativa completa e non un riassunto: ogni nodo, ogni filo e ogni impostazione sono raggiungibili lì senza mouse.

I nodi

Dodici tipi, e l’elenco è breve di proposito. Ogni tipo è un pannello, nove traduzioni, un ramo nell’esecutore e una riga nella tabella di verità, quindi un tipo che si guadagna il posto vale più di uno che ci arriva quasi.

Tipi di nodo, le loro uscite e i loro limiti
NodoUsciteChe cosa fa e dove si ferma
triggeroutL’unica entrata. Il suo evento decide quali variabili esistono. Un innesco manuale porta inoltre il JSON che si inietta: al massimo 2048 caratteri, 40 chiavi e 512 caratteri per valore.
scriptok · errorRende un modello di script verso una destinazione. Esce da ok, oppure da error quando la resa fallisce. Spuntando la prosecuzione dopo un errore, un fallimento prosegue comunque.
switchrule-0 … · otherwiseGuarda un valore e prende la prima regola che si adatta; tutto il resto esce da otherwise. Al massimo dodici regole, e un valore di confronto di al massimo 512 caratteri.
iftrue · falseUna condizione, due uscite. Gli stessi operatori e la stessa valutazione di una regola switch, disegnati come il sì o no che sono davvero.
setoutScrive in state, da dove un nodo successivo o uno script lo rilegge. Al massimo dodici assegnazioni per nodo e 4096 caratteri per valore. I nomi li sceglie chi disegna; http è riservato.
httpok · errorChiama una URL e mette la risposta in state.http. GET o POST, al massimo otto intestazioni, un tempo limite tra 0,5 e 10 secondi e un tetto di risposta tra 256 byte e 256 KiB, 64 KiB per impostazione predefinita. Al massimo tre reindirizzamenti, ognuno filtrato di nuovo.
notifyoutScrive una notifica nel pannello di questo account, al livello informazione o avviso. Titolo di al massimo 120 caratteri, testo di al massimo 1000. Spuntando Inviare anche via e-mail, esce anche come posta elettronica verso tutte le persone con accesso agli script che non hanno disattivato Notifiche dei flussi in Notifiche via e-mail. Un’esecuzione di prova non manda mai posta.
actionok · errorAvvia, ferma, spegne, sospende o riprende il server di questa esecuzione, e attende fino a 300 secondi la conclusione del comando. Zero significa non attendere: accettato non è la stessa cosa di in funzione.
delayoutMette in pausa l’esecuzione. Fino a 30 secondi dentro la richiesta che l’ha avviata; oltre, l’esecuzione viene messa da parte e ripresa più tardi, al massimo 30 giorni.
approvalapproved · rejectedFerma l'esecuzione finché una persona non approva o rifiuta, con un termine e un esito scelto se nessuno risponde.
foreachloop · donePercorre l'uscita del ciclo una volta per elemento di un elenco, letto come JSON, uno per riga o separati da virgole.
repeatloop · donePercorre l'uscita del ciclo finché una condizione è vera e rilegge quella condizione prima di ogni giro.
break—Esce dal ciclo in cui si trova; l'esecuzione continua all'uscita Fine di quel ciclo.
subflowok · errorEsegue un altro flusso di questo account e ne attende l’esito. Il figlio riceve il suo input come input.*.
logoutScrive una riga nell’esecuzione, come fa il nodo di debug in Node-RED. Il campo accetta 1000 caratteri; nell’esecuzione finiscono i primi 200.
note—Testo sulla tela. Non ha porte e non viene mai percorso, quindi non compare nemmeno mai in un’esecuzione.
end—Chiude questo ramo e dice come è andata l’esecuzione: riuscita, fallita, oppure saltata per un server a cui questo flusso non era rivolto. L’accompagna un messaggio di al massimo 280 caratteri.
Un nodo action e un nodo http hanno un’uscita error per la stessa ragione: riguardano qualcun altro, un agente o il server di un terzo, e solo chi ha disegnato il flusso sa se un fallimento lì è grave.

Inneschi

Cinque momenti, e quello che si sceglie non dice soltanto quando il flusso gira. Dice anche quali variabili esistono, quali il selettore propone e quali la piattaforma rifiuta di salvare. Un percorso che in quel momento non può esistere è un errore al salvataggio, e non una stringa vuota a metà esecuzione.

I cinque inneschi e ciò di cui ognuno dispone
InnescoQuando giraChe cosa c’è e che cosa no
server.provisionedUna volta, subito dopo che un ordine ha prodotto la macchina.Tutto. È l’unico momento in cui esiste ogni gruppo.
server.reinstalledDopo che la reinstallazione di una macchina esistente è riuscita.La stessa macchina, un’altra immagine. L’indirizzo non cambia e la data di creazione resta quella originale. Oggi scatta solo per i contenitori; la reinstallazione di una macchina virtuale la conclude un componente che non sa rendere.
server.deletedAppena una cancellazione viene accettata, finché la riga è ancora leggibile.Tutto tranne l’immagine. Non resta più nulla da cui leggere un’immagine, e la piattaforma non se la inventa: os quindi manca e basta.
manualMai da solo. Lo si avvia dall’editor con Inject.Una macchina esistente che si indica, più il proprio JSON come input. Nemmeno qui c’è un’immagine: che cosa sia installato su una macchina la piattaforma non lo può recuperare a posteriori.
webhookAppena un estraneo invia una richiesta a una URL tutta sua.Nessuna macchina, quindi niente piano, niente indirizzo, niente collegamento e niente agente. Restano l’account, il flusso, il corpo inviato come input e la richiesta stessa come webhook.
Un piano appende a ciascuno dei tre momenti di server al massimo un flusso, quindi provisioned, reinstalled e deleted ne portano uno ciascuno e mai un elenco. Un flusso manuale o webhook non si può appendere a un piano: nessuno dei due viene avviato da un ordine, ed è esattamente a questo che servono quei due.

Variabili

Ogni valore che un flusso può leggere viene da uno di questi gruppi. Il selettore dell’editor mostra i gruppi che esistono per l’innesco scelto, e nient’altro. Leggere un percorso sconosciuto è un errore e mai un valore vuoto: un presupposto sbagliato deve fare rumore.

I gruppi di variabili, quando esistono e che cosa contengono
GruppoEsiste conChe cosa contiene
org.*Qualsiasi innescoL’account: id, nome e nome breve.
user.*I quattro di serverLa persona per cui l’esecuzione agisce: id, nome e indirizzo di posta.
flow.*Qualsiasi innescoQuesto flusso e questa esecuzione: nome, id di esecuzione, numero e totale dei passi, innesco e ora di inizio.
server.*I quattro di serverLa macchina: nome, nome host, tipo, stato, id esterno, nodo, core, memoria, disco e data di creazione.
server.compute.*I quattro di serverChe genere di ospite è e che cosa quel genere sa davvero fare: genere, modello, unprivileged, archiviazione, punti di montaggio, capabilities e unsupported. Tutti e sette sono facoltativi, e un’assenza significa non accertato e non un no.
plan.*I quattro di serverIl piano dietro l’ordine: nome, core, memoria, disco e ponte.
os.*Creata e reinstallataL’immagine: nome, nome breve e categoria. Non ci sono famiglia né versione, perché il catalogo non ha quelle colonne e indovinarle sceglie il gestore di pacchetti sbagliato.
ip.*I quattro di serverL’indirizzo assegnato, e se è di versione 4 o 6.
network.*I quattro di serverTutto ciò che circonda quell’indirizzo: cidr, gateway, maschera, rete, broadcast e i resolver.
prefix.*I quattro di serverIl lotto di indirizzi da cui proviene, con il suo intervallo e il suo genitore.
integration.*I quattro di serverIl collegamento che regge la macchina: nome, tipo, nodo, archiviazione, ponte e l’intervallo di vmid.
agent.*I quattro di serverL’agente che ha fatto il lavoro: nome, stato, versione, nome host, sistema operativo, kernel e core.
input.*Manuale e webhookCiò che si è iniettato, o ciò che il webhook ha inviato. Le chiavi sono proprie, e il selettore le segue.
webhook.*Solo webhookLa richiesta stessa: quale punto di ingresso, quando è arrivata e da quale indirizzo.
state.*Dopo un nodo setCiò che un nodo set di questo flusso ha scritto. I nomi sono propri, ed esistono solo al di sotto del nodo che li scrive.
state.http.<node>.*Dopo un nodo httpPer ogni nodo http: status, ok, body, json, error e url. Tutti e sei esistono sempre, sulla strada buona come su quella cattiva.
device.*Ancora in nessunoPensato per apparati di rete. Non esiste un fornitore che possa riempirlo, quindi oggi nessun flusso lo può usare.
Un valore assente e un valore vuoto non sono la stessa cosa. I gruppi che per l’innesco scelto non esistono vengono rimossi anziché svuotati, e attorno a un campo facoltativo va una verifica prima di leggerlo.

Espressioni

Un nodo set scrive in state. Il suo valore è testo letterale, a meno che non si spunti la casella espressione; allora viene letto come modello, con le variabili di questa esecuzione già inserite.

  • Quella casella è una scelta deliberata. Un valore che per caso contiene due graffe, un pezzo di Ansible o un frammento di Helm, continua a significare ciò che diceva. Nulla cambia senso a causa di una modifica che nessuno ha chiesto.
  • Il linguaggio è solo uscita. Due graffe e un percorso, e nient’altro: niente assign, niente capture, niente ciclo, niente include. Ciò che sta sulla tela è ciò che ne esce, con i valori inseriti.
  • Ci sono 46 filtri: testo, numeri, date, i pochi filtri di elenco che producono testo, e i propri quote, ini e ipoctet. Il selettore dell’editor li elenca. Due famiglie mancano di proposito, quelle che tirano a sorte e quelle che restituiscono un oggetto dove si voleva una parola.
  • I segreti qui non sono disponibili, e nemmeno i frammenti riutilizzabili. Ciò che un nodo set scrive finisce nel resoconto dell’esecuzione, e un segreto lì non ha nulla da fare.
Il titolo e il testo di un nodo notify vengono letti sempre come modello, senza casella. Sono prosa, e inserirvi un nome è tutto il loro senso.

Approvazioni e cicli

Un flusso può fermarsi e attendere una persona, e può percorrere gli stessi nodi più di una volta. Nessuna delle due cose era possibile finché un'esecuzione non poteva essere messa in pausa e risvegliata; quella coda ora esiste.

  • Un nodo di approvazione ferma l'esecuzione, crea una richiesta e avvisa l'account. Rispondere richiede un diritto separato, così chi ha scritto il flusso non è automaticamente chi lo firma.
  • Ogni richiesta porta un termine e ciò che accade senza risposta: continuare come approvato, continuare come rifiutato oppure terminare l'esecuzione come non applicabile. Non esiste una quarta scelta, perché un'esecuzione che attende per sempre è un'esecuzione di cui nessuno sente più parlare.
  • Per ogni percorre la sua uscita del ciclo una volta per elemento di un elenco. Nel ciclo, state.loop contiene l'elemento corrente, la sua posizione e il totale.
  • Nulla dall'esterno di un ciclo può essere collegato ai nodi al suo interno. Quei nodi vengono eseguiti a ogni giro e una notifica collegata a entrambi scriverebbe un messaggio per giro.
  • Un passo di approvazione può anche inviare e-mail. Quella posta raggiunge solo i membri che possono decidere sulle approvazioni, non tutti quelli che vedono i flussi, e la notifica nel pannello viene scritta comunque.
Ogni ciclo porta un limite rigido al numero di giri e un'esecuzione uno al numero di passaggi. Oltre il limite l'esecuzione si ferma con un errore invece di svolgere in silenzio una parte del lavoro: un flusso che ha percorso cento elementi su mille senza dire nulla è peggio di uno fallito.

Attendere, e flussi che chiamano flussi

Un’esecuzione ferma non è un’esecuzione fallita. Tre cose possono metterla da parte, e tutte e tre usano la stessa coda: un’approvazione, una pausa lunga e un sottoflusso il cui figlio a sua volta attende.

  • Una pausa di mezzo minuto o meno attende semplicemente dentro la richiesta che l’ha avviata, ed è ciò che serve quando concede un attimo a un agente. Tutto ciò che dura di più mette da parte l’esecuzione, così l’ordine che l’ha avviata si conclude subito e il resto del flusso arriva dopo.
  • Un sottoflusso esegue un altro flusso di questo account come un solo passo. Il suo esito determina l’uscita: un figlio riuscito, o che non ha trovato nulla da fare, prosegue su OK; uno fallito su Errore. Lo stato, l’esecuzione e il nome del flusso stanno in state.subflow.
  • Un flusso non deve mai raggiungere sé stesso, né direttamente né tramite un altro flusso. Viene verificato al deploy, e il messaggio indica la catena trovata.
  • I flussi possono chiamarsi al massimo su tre livelli. Poiché ogni flusso si modifica separatamente, quel limite viene verificato due volte: al deploy e mentre l’esecuzione procede davvero.
Un figlio che si ferma su un’approvazione ferma anche il genitore, e la pagina di ciascuna esecuzione rimanda all’altra. Nulla va perso mentre un’esecuzione attende: il percorso a metà viene conservato e riprende esattamente con il grafo di partenza.

Webhook

Un punto di ingresso webhook dà a un sistema esterno una URL tutta sua. Quel sistema invia JSON, il flusso dietro la URL gira, e la risposta è un codice di stato nudo. I punti di ingresso stanno sotto Script, nella scheda Webhook, e sono oggetti a sé: lo stesso punto di ingresso può puntare a un altro flusso senza che il mittente se ne accorga.

  1. Creare il punto di ingresso e dargli un nome riconoscibile in seguito. La URL viene generata e non si può scegliere: 32 caratteri casuali, cioè 160 bit, quindi indovinarla non è una strategia.
  2. Scegliere come il mittente si identifica, e copiare il segreto. Viene mostrato una sola volta e mai più; se va perso lo si rigenera, e allora cambia anche la URL.
  3. Puntare il punto di ingresso a un flusso il cui innesco sia un webhook. Il punto di ingresso può esistere prima del flusso, e fino ad allora una richiesta viene accettata e riceve un 202 che non avvia nulla. È l’ordine in cui la gente costruisce davvero queste cose, quindi non è trattato come un errore.
I tre modi in cui un mittente si può identificare
SchemaChe cosa fa
noneNessuna prova: chi conosce la URL può avviare il flusso. Difendibile con accanto un elenco di indirizzi, difficile da difendere senza.
hmac_sha256Il mittente firma il corpo grezzo insieme a una marca temporale. Questa è la scelta da fare: lega la richiesta al proprio corpo, così una richiesta intercettata non si può modificare e rispedire.
bearerIl mittente mette il gettone condiviso nell’intestazione Authorization. Semplice, e non dice nulla del corpo che lo accompagnava.

La firma è la parola sha256, un segno di uguale e l’HMAC-SHA256 su esattamente i byte della marca temporale, di un punto e del corpo, in esadecimale minuscolo. Va in X-Provibr-Signature, e la marca temporale stessa va in X-Provibr-Timestamp, in secondi interi dall’epoca.

Una richiesta la cui marca temporale sia più vecchia di 300 secondi, o stia nel futuro, viene rifiutata con un 401. Quella finestra è una regola vera e non un ornamento, perché la marca temporale sta dentro il materiale firmato: una richiesta intercettata non si può rispedire domani incollandoci sopra una marca fresca.

Firmare una richiesta è una riga di openssl. Questo esempio invia un piccolo oggetto; il testo per il terminale resta in inglese di proposito, perché corrisponda a ciò che il terminale mostra.

shell
# Sign the raw body together with the timestamp, then post it.
SECRET="$PROVIBR_WEBHOOK_SECRET"
BODY='{"reason":"maintenance","attempt":1}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)

curl -sS -X POST 'https://app.provibr.com/api/hooks/flows/<your-slug>' \
  -H 'Content-Type: application/json' \
  -H "X-Provibr-Timestamp: $TS" \
  -H "X-Provibr-Signature: sha256=$SIG" \
  --data "$BODY"
Che cosa accetta un punto di ingresso
LimiteDettagli
body64 KiB per impostazione predefinita, regolabile tra 1 KiB e 256 KiB. Una richiesta più grande viene troncata mentre sta ancora arrivando, non letta per intero e poi rifiutata.
rate60 richieste al minuto per impostazione predefinita, regolabili tra 1 e 600. Il conteggio è per punto di ingresso e non per mittente, perché una fonte vera ha un indirizzo e un attaccante ne ha mille.
payloadUn oggetto JSON di un solo livello. Ogni chiave diventa input seguito da quella chiave; un oggetto annidato viene rifiutato anziché appiattito.
allowlistAl massimo 20 indirizzi o intervalli, in v4 o v6. Vuoto significa nessuna restrizione.
endpointsAl massimo 50 punti di ingresso per account.
Che cosa significa ogni risposta
CodiceSignificato
202Accettata. O è partita un’esecuzione, oppure non c’era nulla da far partire: nessun flusso ancora collegato, il flusso è spento, oppure l’esecuzione stessa è caduta. In tutti e quattro i casi il mittente non ha sbagliato nulla, e una ricevuta dice quale dei quattro fosse.
401La firma o il gettone manca o non torna, oppure la firma torna e la sua marca temporale cade fuori dalla finestra.
403Il punto di ingresso è spento, oppure l’indirizzo di partenza non è nell’elenco.
404Non esiste alcun punto di ingresso con questo identificatore.
410Il punto di ingresso è stato revocato. L’identificatore resta occupato per sempre, e questa risposta dice a una fonte onesta che le serve una URL nuova, cosa che un 404 non farebbe.
413Il corpo è più grande di quanto questo punto di ingresso accetti.
422Il corpo non è un oggetto JSON di un solo livello.
429Oltre il tetto di richieste di questo punto di ingresso.

Ogni richiesta lascia una ricevuta, accettata o no: l’esito, lo stato restituito, l’indirizzo di partenza per intero e quanti byte sono stati letti davvero. Il corpo non viene mai conservato. Le ricevute spariscono dopo 90 giorni, prima delle esecuzioni stesse, perché un indirizzo di partenza è un dato personale di qualcuno che non è nostro cliente.

Spegnere un punto di ingresso non è la stessa cosa che revocarlo. Spento risponde 403 e si può riaccendere; revocato risponde 410 per sempre, e la URL non viene mai più assegnata.

Revisioni ed esecuzioni

Due domande che si somigliano hanno qui due risposte diverse. Chi ha cambiato che cosa, e quando, è una revisione. Che cosa questa singola esecuzione abbia davvero percorso è una copia conservata sull’esecuzione stessa. Un’istantanea non può rispondere alla prima domanda e una revisione non può rispondere alla seconda, quindi esistono entrambe.

  • Ogni pubblicazione scrive una revisione, anche una pubblicazione che non ha cambiato nulla. Il numero sale per flusso e non viene mai riusato.
  • La differenza tra due revisioni è un elenco leggibile e non JSON grezzo: un nodo aggiunto, tolto o modificato con i campi nominati, un filo aggiunto o tolto. Dove sta un nodo sulla tela non è una modifica.
  • Ripristinare non riavvolge. Riscrive il vecchio disegno come nuova revisione sopra la storia, con una nota su da dove veniva.
  • Ogni esecuzione conserva il disegno che ha percorso. Modificare un flusso in seguito non riscrive mai ciò che un’esecuzione più vecchia ha fatto.
  • Un’esecuzione ha una riga per ogni nodo visitato, con l’esito e l’uscita da cui è passata, e una riga per ogni nodo non visitato con il motivo. Non visitato è un fatto che vale la pena annotare, non un’assenza.
  • L’elenco delle esecuzioni filtra per innesco, stato e revisione, e si esporta in CSV. L’esportazione si ferma a 5000 righe e lo dice dentro il file anziché finire in silenzio.

I modelli di script tengono accanto una storia propria: ogni versione salvata del contenuto, e una differenza riga per riga tra due qualsiasi di esse.

Un'esecuzione che raggiunge un'approvazione viene messa in pausa: il percorso viene salvato, l'esecuzione passa in attesa e un worker la riprende non appena qualcuno decide o scade il termine. Il resto viene ancora percorso all'interno della richiesta che l'ha avviata, ed è per questo che una pausa arriva a cinque minuti.