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.
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.
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.
| Nodo | Uscite | Che cosa fa e dove si ferma |
|---|---|---|
trigger | out | L’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. |
script | ok · error | Rende 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. |
switch | rule-0 … · otherwise | Guarda 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. |
if | true · false | Una condizione, due uscite. Gli stessi operatori e la stessa valutazione di una regola switch, disegnati come il sì o no che sono davvero. |
set | out | Scrive 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. |
http | ok · error | Chiama 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. |
notify | out | Scrive 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. |
action | ok · error | Avvia, 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. |
delay | out | Mette 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. |
approval | approved · rejected | Ferma l'esecuzione finché una persona non approva o rifiuta, con un termine e un esito scelto se nessuno risponde. |
foreach | loop · done | Percorre l'uscita del ciclo una volta per elemento di un elenco, letto come JSON, uno per riga o separati da virgole. |
repeat | loop · done | Percorre 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. |
subflow | ok · error | Esegue un altro flusso di questo account e ne attende l’esito. Il figlio riceve il suo input come input.*. |
log | out | Scrive 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. |
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.
| Innesco | Quando gira | Che cosa c’è e che cosa no |
|---|---|---|
server.provisioned | Una volta, subito dopo che un ordine ha prodotto la macchina. | Tutto. È l’unico momento in cui esiste ogni gruppo. |
server.reinstalled | Dopo 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.deleted | Appena 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. |
manual | Mai 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. |
webhook | Appena 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. |
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.
| Gruppo | Esiste con | Che cosa contiene |
|---|---|---|
org.* | Qualsiasi innesco | L’account: id, nome e nome breve. |
user.* | I quattro di server | La persona per cui l’esecuzione agisce: id, nome e indirizzo di posta. |
flow.* | Qualsiasi innesco | Questo flusso e questa esecuzione: nome, id di esecuzione, numero e totale dei passi, innesco e ora di inizio. |
server.* | I quattro di server | La macchina: nome, nome host, tipo, stato, id esterno, nodo, core, memoria, disco e data di creazione. |
server.compute.* | I quattro di server | Che 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 server | Il piano dietro l’ordine: nome, core, memoria, disco e ponte. |
os.* | Creata e reinstallata | L’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 server | L’indirizzo assegnato, e se è di versione 4 o 6. |
network.* | I quattro di server | Tutto ciò che circonda quell’indirizzo: cidr, gateway, maschera, rete, broadcast e i resolver. |
prefix.* | I quattro di server | Il lotto di indirizzi da cui proviene, con il suo intervallo e il suo genitore. |
integration.* | I quattro di server | Il collegamento che regge la macchina: nome, tipo, nodo, archiviazione, ponte e l’intervallo di vmid. |
agent.* | I quattro di server | L’agente che ha fatto il lavoro: nome, stato, versione, nome host, sistema operativo, kernel e core. |
input.* | Manuale e webhook | Ciò che si è iniettato, o ciò che il webhook ha inviato. Le chiavi sono proprie, e il selettore le segue. |
webhook.* | Solo webhook | La richiesta stessa: quale punto di ingresso, quando è arrivata e da quale indirizzo. |
state.* | Dopo un nodo set | Ciò 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 http | Per 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 nessuno | Pensato per apparati di rete. Non esiste un fornitore che possa riempirlo, quindi oggi nessun flusso lo può usare. |
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.
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.
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.
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.
- 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.
- 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.
- 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.
| Schema | Che cosa fa |
|---|---|
none | Nessuna prova: chi conosce la URL può avviare il flusso. Difendibile con accanto un elenco di indirizzi, difficile da difendere senza. |
hmac_sha256 | Il 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. |
bearer | Il 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.
# 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"| Limite | Dettagli |
|---|---|
body | 64 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. |
rate | 60 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. |
payload | Un oggetto JSON di un solo livello. Ogni chiave diventa input seguito da quella chiave; un oggetto annidato viene rifiutato anziché appiattito. |
allowlist | Al massimo 20 indirizzi o intervalli, in v4 o v6. Vuoto significa nessuna restrizione. |
endpoints | Al massimo 50 punti di ingresso per account. |
| Codice | Significato |
|---|---|
202 | Accettata. 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. |
401 | La firma o il gettone manca o non torna, oppure la firma torna e la sua marca temporale cade fuori dalla finestra. |
403 | Il punto di ingresso è spento, oppure l’indirizzo di partenza non è nell’elenco. |
404 | Non esiste alcun punto di ingresso con questo identificatore. |
410 | Il 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. |
413 | Il corpo è più grande di quanto questo punto di ingresso accetti. |
422 | Il corpo non è un oggetto JSON di un solo livello. |
429 | Oltre 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.
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.