Naar de inhoud

Documentatie

Flows en automatisering

Een flow is een tekening die draait. Je zet knopen op een canvas, trekt er draden tussen, en het platform loopt die tekening af zodra het moment aanbreekt dat je gekozen hebt. Deze pagina zegt wat een flow kan, wat elke knoop betekent, en waar precies zijn grenzen liggen.

Wat een flow is

Een flow is een gerichte graaf: knopen met draden ertussen, afgelopen vanaf één beginpunt. Het is bewust geen script met een lus. Vier regels leggen de vorm vast, en de editor weigert een tekening te publiceren die er een van breekt, met de knoop erbij die niet klopt.

  • Precies één trigger. De triggerknoop is de enige ingang, en hij bepaalt welke variabelen er in de rest van de tekening bestaan.
  • Geen cycli. Een draad die terugleidt naar een knoop waar hij vandaan kwam wordt geweigerd, want een flow kan niet wachten: een lus zou eenvoudigweg nooit eindigen.
  • Elke knoop moet vanaf de trigger bereikbaar zijn. Een knoop die los hangt is een fout die je op een schermafbeelding niet ziet, dus het is een fout bij het publiceren.
  • Takken worden na elkaar afgelopen, nooit naast elkaar. Twee takken tegelijk zouden tegelijk in state schrijven, en de uitkomst zou van de timing afhangen.
Een tekening bevat hoogstens 80 knopen en 160 draden, waarvan er hoogstens 25 werkelijk iets doen. Dat laatste getal is hetzelfde dat de oude staplijsten hadden; tekenen in plaats van opsommen verhoogt het niet.

Het canvas

De editor is een canvas en er is geen tweede. Een nieuwe flow opent er meteen op, en een flow die een rechte lijst met stappen was opent als een rechte keten van knopen.

  • Het palet bevat de twaalf knoopsoorten. Klik er een aan om hem op het canvas te zetten, of sleep hem naar de plek die je wilt.
  • Versleep een knoop aan zijn kop om hem te verplaatsen. Al het andere op de knoop is een bedieningselement en begint dus geen sleepbeweging.
  • Een draad loopt van een uitgangsstip naar een ingangsstip. Sleep van de een naar de ander, of klik eerst de uitgang en dan de ingang aan. Allebei doen hetzelfde, en het tweede is de toetsenbordweg.
  • Laat een draad in het niets los en er gebeurt niets. Verwijderen doe je door hem te selecteren en op Delete te drukken, of met het kruisje dat erop staat.
  • Zoom tussen 50 en 200 procent, gebruik Passend om de hele tekening te zien, en versleep de achtergrond om te pannen. Er is geen minimap.
De lijstweergave is dezelfde graaf als tekst, met hetzelfde eigenschappenpaneel ernaast. Het is een volwaardig alternatief en geen samenvatting: elke knoop, elke draad en elke instelling is er zonder muis bereikbaar.

De knopen

Twaalf soorten, en die lijst is met opzet kort. Elke soort is een paneel, negen vertalingen, een tak in de uitvoerder en een rij in de waarheidstabel, dus een soort die zijn gewicht draagt is meer waard dan een soort die dat bijna doet.

Knoopsoorten, hun uitgangen en hun grenzen
KnoopUitgangenWat hij doet, en waar hij ophoudt
triggeroutDe enige ingang. Zijn gebeurtenis bepaalt welke variabelen bestaan. Een handmatige trigger draagt ook de JSON die je injecteert: hoogstens 2048 tekens, 40 sleutels en 512 tekens per waarde.
scriptok · errorRendert een scriptsjabloon tegen een doel. Hij verlaat de knoop via ok, of via error als het renderen mislukt. Vink doorgaan bij een fout aan en een mislukking loopt tóch door.
switchrule-0 … · otherwiseKijkt naar één waarde en neemt de eerste regel die past; al het andere gaat via otherwise. Hoogstens twaalf regels, en een vergeleken waarde van hoogstens 512 tekens.
iftrue · falseEén voorwaarde, twee uitgangen. Dezelfde operatoren en dezelfde evaluatie als een switch-regel, getekend als de ja-of-nee die het werkelijk is.
setoutSchrijft in state, waar een latere knoop of een script het weer uitleest. Hoogstens twaalf toekenningen per knoop en 4096 tekens per waarde. De namen kies je zelf; http is gereserveerd.
httpok · errorRoept een URL aan en zet het antwoord in state.http. GET of POST, hoogstens acht headers, een timeout tussen 0,5 en 10 seconden, en een antwoordgrens tussen 256 bytes en 256 KiB, standaard 64 KiB. Hoogstens drie redirects, elk opnieuw gezeefd.
notifyoutSchrijft een melding in het paneel van dit account, op niveau info of waarschuwing. Kop hoogstens 120 tekens, tekst hoogstens 1000. Vink Ook per e-mail versturen aan en hij gaat er ook per e-mail uit, naar iedereen met toegang tot Scripts die Flowmeldingen niet heeft uitgezet onder E-mailmeldingen. Een proefrun verstuurt nooit e-mail.
actionok · errorStart, stopt, sluit af, schort op of hervat de server waar deze run over gaat, en wacht tot 300 seconden op de afronding van het commando. Nul betekent niet wachten: aanvaard is niet hetzelfde als draaiend.
delayoutPauzeert de run. Tot 30 seconden binnen het verzoek dat hem startte; langer en de run wordt weggelegd en later opgepakt, tot hoogstens 30 dagen.
approvalapproved · rejectedLegt de run stil tot een mens goedkeurt of afwijst, met een deadline en een gekozen uitkomst als niemand antwoordt.
foreachloop · doneLoopt de lusuitgang één keer per element van een lijst af, gelezen als JSON, één per regel, of gescheiden door komma's.
repeatloop · doneLoopt de lusuitgang af zolang een voorwaarde waar is, en leest die voorwaarde vóór elke ronde opnieuw.
break—Verlaat de lus waar hij in staat; de run gaat verder bij de klaar-uitgang van die lus.
subflowok · errorVoert een andere flow van dit account uit en wacht op de uitkomst. Het kind krijgt zijn invoer als input.*.
logoutSchrijft één regel in de run, zoals de debug-knoop dat in Node-RED doet. Het veld bevat 1000 tekens; wat in de run belandt zijn de eerste 200.
note—Tekst op het canvas. Hij heeft geen poorten en wordt nooit afgelopen, dus hij komt ook nooit in een run voor.
end—Beëindigt deze tak en zegt hoe de run afliep: geslaagd, mislukt, of overgeslagen voor een server waar deze flow niet voor bedoeld was. Er hoort een boodschap van hoogstens 280 tekens bij.
Een action- en een http-knoop hebben een error-uitgang om dezelfde reden: ze gaan over iemand anders, een agent of de server van een vreemde, en alleen wie de flow tekende weet of een mislukking daar erg is.

Triggers

Vijf momenten, en welk moment je kiest is niet alleen wanneer de flow draait. Het is ook welke variabelen bestaan, welke de kiezer je aanbiedt, en welke het platform weigert op te slaan. Een pad dat op dat moment niet kán bestaan is een fout tijdens het opslaan, geen lege tekst midden in een run.

De vijf triggers en wat elk van hen heeft
TriggerWanneer hij draaitWat er is, en wat niet
server.provisionedEén keer, meteen nadat een bestelling de machine heeft opgeleverd.Alles. Dit is het enige moment waarop elke groep bestaat.
server.reinstalledNadat een herinstallatie van een bestaande machine geslaagd is.Dezelfde machine, een ander image. Het adres is ongewijzigd en de aanmaakdatum is nog steeds de oorspronkelijke. Vandaag vuurt hij alleen voor containers; een herinstallatie van een virtuele machine wordt afgerond door een onderdeel dat niet kan renderen.
server.deletedZodra een verwijdering aanvaard is, terwijl de rij nog leesbaar is.Alles behalve het image. Er is niets meer om een image uit te lezen, en het platform verzint er geen, dus os ontbreekt gewoon.
manualNooit uit zichzelf. Je start hem in de editor met Inject.Een bestaande machine die je aanwijst, plus je eigen JSON als input. Ook hier is er geen image: wat er op een machine staat kan het platform achteraf niet opzoeken.
webhookZodra een vreemde naar een eigen URL post.Helemaal geen machine, dus geen plan, geen adres, geen koppeling en geen agent. Wat overblijft is het account, de flow, de geposte body als input, en het verzoek zelf als webhook.
Een plan hangt aan elk van de drie servermomenten hoogstens één flow, dus provisioned, reinstalled en deleted dragen er elk één en nooit een lijst. Een handmatige of webhookflow kan helemaal niet aan een plan hangen: geen van beide wordt door een bestelling gestart, en daar zijn die twee ook precies voor.

Variabelen

Elke waarde die een flow kan lezen komt uit een van deze groepen. De kiezer in de editor toont de groepen die bij je trigger bestaan en verder niets. Een onbekend pad lezen is een fout en nooit een lege waarde: een verkeerde aanname hoort luid te zijn.

De variabelegroepen, wanneer ze bestaan, en wat ze bevatten
GroepBestaat bijWat erin zit
org.*Elke triggerHet account: id, naam en slug.
user.*De vier servertriggersDe persoon voor wie de run handelt: id, naam en e-mailadres.
flow.*Elke triggerDeze flow en deze run: naam, run-id, stapnummer en aantal, trigger, en wanneer hij begon.
server.*De vier servertriggersDe machine: naam, hostnaam, soort, status, extern id, node, cores, geheugen, schijf en aanmaakdatum.
server.compute.*De vier servertriggersWat voor gast het is en wat die soort werkelijk kan: soort, template, unprivileged, opslag, mountpoints, capabilities en unsupported. Alle zeven zijn optioneel, en ontbreken betekent niet vastgesteld en niet nee.
plan.*De vier servertriggersHet plan achter de bestelling: naam, cores, geheugen, schijf en bridge.
os.*Aangemaakt en herinstalleerdHet image: naam, slug en categorie. Er is geen familie en geen versie, want de catalogus heeft die kolommen niet en ze raden kiest de verkeerde pakketbeheerder.
ip.*De vier servertriggersHet adres dat is uitgegeven, en of het versie 4 of 6 is.
network.*De vier servertriggersAlles rond dat adres: cidr, gateway, netmasker, netwerk, broadcast en de resolvers.
prefix.*De vier servertriggersDe adrespool waar het uit komt, met zijn bereik en zijn ouder.
integration.*De vier servertriggersDe koppeling die de machine draagt: naam, soort, node, opslag, bridge en het vmid-bereik.
agent.*De vier servertriggersDe agent die het werk deed: naam, status, versie, hostnaam, besturingssysteem, kernel en cores.
input.*Handmatig en webhookWat je injecteerde, of wat de webhook postte. De sleutels zijn van jou, en de kiezer volgt ze.
webhook.*Alleen webhookHet verzoek zelf: welk endpoint, wanneer het binnenkwam, en van welk adres.
state.*Na een set-knoopWat een set-knoop in deze flow schreef. De namen zijn van jou, en ze bestaan alleen onder de knoop die ze schrijft.
state.http.<node>.*Na een http-knoopPer http-knoop: status, ok, body, json, error en url. Alle zes bestaan altijd, op het goede pad en op het slechte.
device.*Nog nergensOntworpen voor netwerkapparatuur. Er is geen provider die hem kan vullen, dus geen enkele flow kan hem vandaag gebruiken.
Een ontbrekende waarde en een lege waarde zijn niet hetzelfde. Groepen die bij je trigger niet bestaan worden verwijderd in plaats van leeggemaakt, en om een optioneel veld hoort een controle voordat je het leest.

Expressies

Een set-knoop schrijft in state. Zijn waarde is letterlijke tekst, tenzij je het vinkje voor expressies zet; dan wordt hij gelezen als een sjabloon, met de variabelen van deze run ingevuld.

  • Dat vinkje is een bewuste keuze. Een waarde die toevallig twee accolades bevat, een stuk Ansible of een fragment Helm, blijft betekenen wat er stond. Er verandert niets van betekenis door een wijziging waar niemand om vroeg.
  • De taal is alleen uitvoer. Twee accolades en een pad, en verder niets: geen assign, geen capture, geen lus, geen include. Wat op het canvas staat is wat eruit komt, met de waarden ingevuld.
  • Er zijn 46 filters: tekst, getallen, datums, de paar lijstfilters die tekst opleveren, en de eigen quote, ini en ipoctet. De kiezer in de editor somt ze op. Twee families ontbreken met opzet, die welke dobbelen en die welke een object teruggeven waar je een woord wilde.
  • Geheimen zijn hier niet beschikbaar, en snippets evenmin. Wat een set-knoop schrijft belandt in het verslag van de run, en daar heeft een geheim niets te zoeken.
De kop en de tekst van een notify-knoop worden altijd als sjabloon gelezen, zonder vinkje. Het is proza, en er een naam in invullen is er de hele bedoeling van.

Goedkeuringen en lussen

Een flow kan stoppen en op een mens wachten, en hij kan dezelfde knopen meer dan eens aflopen. Allebei kon niet zolang een run niet stilgezet en later gewekt kon worden; die wachtrij is er nu.

  • Een goedkeuringsknoop legt de run stil, schrijft een verzoek en meldt het aan het account. Beantwoorden vraagt een apart recht, zodat wie de flow schreef niet vanzelf degene is die hem aftekent.
  • Elk verzoek draagt een deadline en wat er zonder antwoord gebeurt: doorgaan als goedgekeurd, doorgaan als afgewezen, of de run beëindigen als niet van toepassing. Er is geen vierde keuze, want een run die eeuwig wacht is een run waarvan niemand ooit nog hoort.
  • Voor elk loopt zijn lusuitgang één keer per element van een lijst af. In de lus bevat state.loop het huidige element, zijn plaats en het totaal.
  • Er mag niets van buiten een lus aan de knopen erbinnen hangen. Die knopen draaien elke ronde opnieuw, en een melding die aan allebei hangt zou per ronde één bericht schrijven.
  • Een goedkeuringsstap kan ook post versturen. Die gaat alleen naar de leden die over goedkeuringen mogen beslissen, niet naar iedereen die flows kan zien, en de paneelmelding wordt hoe dan ook geschreven.
Elke lus draagt een harde grens op het aantal ronden, en een run draagt er een op het aantal stappen. Boven de grens stopt de run met een fout in plaats van stilzwijgend een deel van het werk te doen: een flow die honderd van de duizend elementen deed en niets zei, is erger dan een die faalde.

Wachten, en flows die flows aanroepen

Een run die stilstaat is geen run die mislukt is. Drie dingen kunnen er een wegleggen, en alle drie gebruiken ze dezelfde wachtrij: een goedkeuring, een lange pauze, en een subflow waarvan het kind zelf staat te wachten.

  • Een pauze van een halve minuut of korter wacht gewoon binnen het verzoek dat hem startte, en dat is wat je wilt als je een agent even de tijd geeft. Alles wat langer duurt legt de run weg, zodat de bestelling die hem startte meteen klaar is en de rest van de flow later volgt.
  • Een subflow voert een andere flow van dit account uit als één stap. Zijn uitkomst bepaalt de uitgang: een kind dat slaagde of niets te doen vond gaat over OK verder, een kind dat mislukte over Fout. De stand, de run en de flownaam staan in state.subflow.
  • Een flow mag zichzelf nooit bereiken, rechtstreeks niet en via een andere flow ook niet. Dat wordt bij het deployen gecontroleerd, en de melding noemt de keten die gevonden is.
  • Flows mogen elkaar hoogstens drie niveaus diep aanroepen. Omdat flows los van elkaar bewerkt worden, wordt die grens twee keer gecontroleerd: één keer bij het deployen en één keer terwijl de run werkelijk loopt.
Een kind dat op een goedkeuring stilvalt legt zijn ouder ook stil, en de runpagina van allebei verwijst naar de ander. Er gaat niets verloren zolang een run wacht: de halve wandeling wordt bewaard, en hij gaat verder met precies de graaf waarmee hij begon.

Webhooks

Een webhook-endpoint geeft een systeem van buiten een eigen URL. Het post JSON, de flow achter de URL draait, en het antwoord is een kale statuscode. Endpoints staan onder Scripts, op het tabblad Webhooks, en zijn eigen objecten: hetzelfde endpoint kan naar een andere flow gaan zonder dat de afzender er iets van merkt.

  1. Maak het endpoint aan en geef het een naam die je later herkent. De URL wordt voor jou gemaakt en is niet te kiezen: 32 willekeurige tekens, oftewel 160 bits, dus raden is geen strategie.
  2. Kies hoe de afzender zich bewijst, en kopieer het geheim. Het wordt één keer getoond en daarna nooit meer; ben je het kwijt, dan regenereer je, en dan krijg je ook een nieuwe URL.
  3. Wijs het endpoint naar een flow met een webhook als trigger. Het endpoint mag eerder bestaan dan de flow, en tot dan wordt een verzoek aanvaard en beantwoord met een 202 die niets start. Dat is de volgorde waarin mensen dit werkelijk bouwen, dus het geldt niet als een fout.
De drie manieren waarop een afzender zich kan bewijzen
SchemaWat het doet
noneHelemaal geen bewijs: wie de URL kent kan de flow starten. Te verdedigen met een adreslijst ernaast, en moeilijk te verdedigen zonder.
hmac_sha256De afzender ondertekent de rauwe body samen met een tijdstempel. Dit is de keuze: hij bindt het verzoek aan zijn eigen body, dus een onderschept verzoek is niet te bewerken en door te sturen.
bearerDe afzender zet het gedeelde token in de Authorization-header. Eenvoudig, en het zegt niets over de body die eraan vastzat.

De handtekening is het woord sha256, een isgelijkteken, en de HMAC-SHA256 over precies de bytes van het tijdstempel, een punt, en de body, in kleine letters hexadecimaal. Hij gaat in X-Provibr-Signature, en het tijdstempel zelf gaat in X-Provibr-Timestamp als hele seconden sinds het epoch.

Een verzoek waarvan het tijdstempel meer dan 300 seconden oud is, of in de toekomst ligt, wordt geweigerd met een 401. Dat venster is een echte regel en geen versiering, want het tijdstempel zit ín het ondertekende materiaal: een onderschept verzoek is morgen niet opnieuw te versturen met een vers tijdstempel eroverheen geplakt.

Een verzoek ondertekenen is één regel openssl. Dit voorbeeld post een klein object; de shell-tekst blijft met opzet Engels, zodat hij overeenkomt met wat je terminal toont.

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"
Wat een endpoint aanneemt
GrensDetails
bodyStandaard 64 KiB, instelbaar tussen 1 KiB en 256 KiB. Een groter verzoek wordt afgekapt terwijl het nog binnenkomt, niet eerst volledig gelezen en dan geweigerd.
rateStandaard 60 verzoeken per minuut, instelbaar tussen 1 en 600. Hij telt per endpoint en niet per afzender, want een echte bron heeft één adres en een aanvaller heeft er duizend.
payloadEen JSON-object van één niveau diep. Elke sleutel wordt input gevolgd door die sleutel; een genest object wordt geweigerd en niet platgeslagen.
allowlistHoogstens 20 adressen of bereiken, in v4 of v6. Leeg betekent helemaal geen beperking.
endpointsHoogstens 50 endpoints per account.
Wat elk antwoord betekent
CodeBetekenis
202Aanvaard. Er is een run gestart, of er was niets te starten: nog geen flow gekoppeld, de flow staat uit, of de run zelf viel om. In alle vier de gevallen deed de afzender niets fout, en een bonnetje zegt welke van de vier het was.
401De handtekening of het token ontbreekt of klopt niet, of de handtekening klopt en zijn tijdstempel valt buiten het venster.
403Het endpoint staat uit, of het bronadres staat niet op de adreslijst.
404Er is geen endpoint met deze slug.
410Het endpoint is ingetrokken. De slug blijft voorgoed bezet, en dit antwoord zegt een eerlijke bron dat hij een nieuwe URL nodig heeft, wat een 404 niet zou doen.
413De body is groter dan dit endpoint aanneemt.
422De body is geen JSON-object van één niveau.
429Over de doorvoerrem van dit endpoint heen.

Elk verzoek laat een bonnetje achter, aanvaard of niet: de uitkomst, de status die terugging, het bronadres voluit, en hoeveel bytes er werkelijk gelezen zijn. De body wordt nooit bewaard. Bonnetjes verdwijnen na 90 dagen, eerder dan de runs zelf, want een bronadres is een persoonsgegeven van iemand die onze klant niet is.

Een endpoint uitzetten is niet hetzelfde als het intrekken. Uit antwoordt 403 en is weer aan te zetten; ingetrokken antwoordt voorgoed 410, en de URL wordt nooit meer uitgegeven.

Revisies en runs

Twee vragen die op elkaar lijken hebben hier twee verschillende antwoorden. Wie wat wanneer veranderde is een revisie. Wat deze ene run werkelijk aflegde is een kopie die op de run zelf staat. Een momentopname kan de eerste vraag niet beantwoorden en een revisie de tweede niet, dus ze bestaan allebei.

  • Elke publicatie schrijft een revisie, ook een publicatie die niets veranderde. Het nummer loopt per flow op en wordt nooit hergebruikt.
  • Het verschil tussen twee revisies is een leesbare lijst en geen rauwe JSON: een knoop erbij, weg of gewijzigd met de velden erbij, een draad erbij of weg. Waar een knoop op het canvas staat is geen wijziging.
  • Herstellen draait niets terug. Het schrijft de oude tekening als een nieuwe revisie bovenop de geschiedenis, met een aantekening waar hij vandaan komt.
  • Elke run bewaart de tekening die hij afliep. Een flow die daarna wijzigt herschrijft nooit wat een oudere run deed.
  • Een run bevat een regel per knoop die bezocht is, met de uitkomst en de uitgang waarlangs hij vertrok, en een regel per knoop die niet bezocht is met de reden. Niet bezocht is een feit dat het opschrijven waard is, geen afwezigheid.
  • De runlijst filtert op trigger, status en revisie, en exporteert naar CSV. De export stopt bij 5000 regels en zegt dat ín het bestand in plaats van stilletjes op te houden.

Scriptsjablonen houden hiernaast een eigen geschiedenis bij: elke bewaarde versie van de inhoud, en een regel-voor-regelverschil tussen twee ervan.

Een run die bij een goedkeuring komt wordt stilgezet: de wandeling wordt bewaard, de run gaat op wachten, en een worker zet hem voort zodra iemand beslist of de deadline verstrijkt. De rest wordt nog steeds binnen het verzoek afgelopen dat hem startte, en daarom loopt een pauze tot vijf minuten.