Săriți la conținut

Blog ·

O limită își are locul în nucleu

Revocarea unui agent găzduit îl desprindea de comanda lui și lăsa pornită o mașină Proxmox la care nimeni nu ajungea și pentru care toți plăteau mai departe.

De Provibr

Un Cloud Agent este varianta agentului nostru în care mașina o rulăm noi. Clientul comandă unul, noi clonăm un șablon Proxmox, instalăm agentul pe el și îl facturăm lunar. Din panou arată ca orice alt agent, iar asemănarea aceea este toată povestea rundei acesteia.

Orice alt agent este al clientului. A-l revoca este o operație curată și lipsită de primejdie: platforma trimite o înștiințare de revocare, agentul își șterge identitatea, certificatul și seiful de credențiale, iar rândul poate fi apoi șters. Nu rămâne nimic în funcțiune nicăieri, fiindcă lucrul care funcționa nu a fost niciodată al nostru.

Pentru un Cloud Agent asta nu este adevărat, iar nimic nu îl oprea.

Ce am măsurat înainte să schimbăm ceva

Pe baza de date de dezvoltare, cu o comandă adevărată în spatele unui agent adevărat, am apelat calea de îndepărtare exact așa cum o face butonul din panou.

A raportat mode: "hard". Rândul agentului dispăruse. Rândul comenzii era încă acolo, încă active, purtând încă vmid 5998, iar agent_id-ul lui devenise în tăcere NULL, fiindcă cheia străină de la comandă către agent este ON DELETE SET NULL.

Așadar exista o mașină virtuală pe un hipervizor, pornită, cu un agent pe ea care își ștersese propria identitate și care nu se mai putea deci conecta niciodată, legată de o comandă care încă genera o taxă lunară, și niciun rând nicăieri care să arate de la una către cealaltă. De neatins, de neoprit din panou și nu gratuită.

Restricția care a produs asta este corectă. Punerea referinței pe NULL este purtarea potrivită pentru o coloană care spune care agent este instalat aici acum; o cascadă ar fi șters comanda și înregistrarea de facturare odată cu ea. Defectul este că nimic nu punea întrebarea pe care schema nu o putea pune.

Două gărzi care existau deja, amândouă privind în partea greșită

Ștergerea unui agent avea deja două refuzuri și merită să fim exacți în privința motivului pentru care niciunul nu s-a declanșat.

Primul refuză ștergerea unui agent care este înrolat în clipa aceea: revocați-l întâi, ca să facă el curat după el. Când rândul urma să fie șters, agentul tocmai fusese revocat în același apel, deci nu era înrolat, iar verificarea era mulțumită.

Al doilea refuză ștergerea unui agent de care mai atârnă integrări sau servere, fiindcă acele chei străine cascadează, iar un singur clic ar lua tot inventarul cu el. Un Cloud Agent nu are nici una, nici alta: este o mașină nouă care există ca să ruleze agentul, deci verificarea a trecut fiindcă nu era nimic de numărat.

Amândouă gărzile erau despre ce ar fi distrus de ștergere. Lucrul care supraviețuiește ștergerii și costă bani tocmai fiindcă supraviețuiește se afla în afara formei întrebării.

Limita merge în nucleu, nu în buton

Reparația evidentă este să verificați în acțiunea de server din spatele butonului. Este și greșită, iar motivul este o breșă în care codul acesta a mai intrat o dată, cu un punct de intrare de autentificare din stoc care ocolea fiecare control de pe partea de server.

Am numărat locurile de apel. În afara celor două funcții de nucleu există șapte apeluri către revocare, ștergere sau îndepărtarea combinată, răspândite în trei fișiere: trei acțiuni de server pe pagina agentului, acțiunea în masă și două rute de API v1. O verificare în acțiunea din panou ar fi lăsat rutele de API deschise, iar o rută de API este exact ce folosește scriptul unui client.

Așa că limita stă înăuntrul lui revokeAgentCore() și deleteAgentCore() însele, gâtuitura prin care trec toate cele șapte apeluri. O funcție, întrebată de două ori.

Stă după ramura idempotentă și nu înainte. Un agent care este deja revocat nu mai are nimic de revocat, iar a-i spune apelantului aceluia să își anuleze întâi comanda ar fi un neadevăr despre un apel care oricum nu va face nimic. O limită se cuvine pusă pe operația care chiar schimbă ceva.

Predicatul este mai poate fi anulată, nu este vie

Există în pachetul comun o listă numită CLOUD_AGENT_LIVE_STATUSES: stările în care o mașină există și costă bani. Are patru membri și este exact lista greșită de refolosit aici.

Predicatul pe care îl folosim este cloudAgentActionAllowed("cancel", status), care este adevărat pentru șapte din cele opt stări ale comenzii, pentru tot în afară de o comandă care este deja anulată.

Cele trei stări din diferență sunt cele interesante, iar failed este cea mai ascuțită. O comandă eșuată nu costă nimic, deci nu este vie. Dar o comandă eșuată este tocmai aceea care s-ar putea să fi ajuns destul de departe încât să creeze o mașină și apoi să se fi prăbușit, motiv pentru care anularea este îngăduită din ea de la bun început. Refolosirea listei de facturare ar fi lăsat nepăzit singurul caz cu o mașină orfană și ar fi arătat cu totul cuminte la revizie.

Două liste care înseamnă aproape același lucru se depărtează. Întrebarea aici nu este costă asta bani, ci mai este anularea calea, iar exact o funcție din cod răspunde la aceea.

Două căi de scriere cărora li se îngăduie să o sară

Garda pe care am scris-o derivă fiecare loc din sursa panoului care revocă, șterge definitiv sau ascunde discret un rând de agent, folosind o parcurgere a arborelui sintactic peste arborii de aplicație și de bibliotecă, și cere pentru fiecare loc fie limita, fie un motiv scris. Două locuri poartă un motiv: însăși calea de anulare, care îndepărtează mașina, eliberează adresa și abia apoi revocă agentul, și închiderea contului, care revocă fiecare agent al unei organizații și tratează separat Cloud Agentul în același flux. A bloca vreuna dintre ele ar închide o ieșire corectă în loc să economisească bani.

Garda are cele două site care împiedică un scanner de surse să iasă verde în gol: o limită de jos pentru câte locuri de scriere trebuie să găsească, trei dintre ele numite, și un test care eșuează pe o intrare din lista de excepții care arată către un fișier ce nu mai scrie nimic. Rulează 4 teste și 11 afirmații.

Două fapte pe ecran, nu unul

Partea de panou avea nevoie de două valori logice separate, iar noi era cât pe ce să livrăm una.

orderOpen este un fapt despre comandă: mai poate fi anulată. canCancel este un fapt despre cel care privește: are voie persoana aceasta să o anuleze. Contopirea lor într-un singur poate lucra aici ar însemna că un membru fără dreptul de anulare vede butonul de revocare, îl folosește, aruncă mașina și nu poate opri factura. Butoanele dispar deci pe faptul despre comandă, iar fișa rămâne cu motivul în ea, fiindcă de ce nu îl pot revoca pe acesta este întrebarea pe care o pune toată lumea care a văzut butonul pe ceilalți agenți ai ei.

Un lucru a venit odată cu schimbarea fiindcă ea l-a făcut neadevărat: instrucțiunile de dezinstalare povățuiau să revocați întâi agentul din panou. Acela este un buton care nu mai există pe pagina aceasta.

Ce am consemna

  1. O asemănare în interfață nu este o asemănare în urmări. Doi agenți, un buton, iar unul dintre ei costă bani după ce îl apăsați.
  2. ON DELETE SET NULL poate fi corect și tot să vă lase cu un orfan. Schema nu poate pune întrebarea.
  3. Gărzile despre ce distruge o ștergere nu vor vedea ce lasă în urmă o ștergere.
  4. Numărați locurile de apel înainte să alegeți unde merge verificarea. Ale noastre erau șapte, în trei fișiere, două dintre ele rute de API.
  5. Puneți limita la gâtuitură, nu pe buton. Un buton este doar una dintre intrări.
  6. Puneți-o după ramura idempotentă. Un refuz pe un apel care nu ar face nimic este o minciună.
  7. Refolosirea unei liste care înseamnă aproape același lucru este un eșec lent. Costă bani și mai poate fi anulată diferă prin trei stări, iar în diferență se află paguba.
  8. O stare care nu costă nimic tot poate să fi lăsat ceva în urmă. Aprovizionarea eșuată este cea primejdioasă.
  9. Despărțiți faptul despre obiect de faptul despre cel care privește. Contopite, dau unui membru cu drepturi puține un buton distrugător.
  10. Lăsați garda să citească arborele. Un test pentru fiecare intrare știută nu poate vedea intrarea pe care o adaugă cineva luna viitoare.

Ca să fie limpede ce este aceasta: defectul a fost reprodus față de baza de date de dezvoltare înainte de orice schimbare, verificarea cap la cap s-a încheiat la 26 verde și fusese dovedită roșie la 16 verde și 10 eșecuri, verificarea de navigator la 29 verde și dovedită roșie la 18 și 11 cu indicatorul comenzii forțat pe oprit, nouă limbi la 178 verde, iar verificarea de îmbinare a rulat fără nicio linie de eșec. Nedovedit: nicio mașină adevărată de client nu a fost implicată, iar partea de facturare a fost cercetată și nu exersată. Trei capcane de măsurare au costat timp și merită numite: zona primejdioasă trăiește în spatele unui parametru de filă, deci o verificare care îl omite măsoară zero butoane și citește asta ca izbândă; o secvență de evadare cu bară oblică inversă înăuntrul unui literal de șablon dat unei evaluări de pagină nu este o evadare; iar unul dintre cele trei scripturi de catalog a aplatizat un spațiu neîntrerupt pe care celelalte două l-au păstrat.

Din munca de platformă la Provibr. De ce o mașină poate fi vândută exact o dată se află în două comenzi, o singură mașină, de ce o scurtătură este o referință și niciodată o permisiune se află în o scurtătură este o referință, nu un drept, iar un cuvânt de stare împrumutat din tabelul greșit se află în o scanare care își închidea propriul slot.

Începeți cu o gazdă și un agent

Creați un cont, instalați agentul cu o singură comandă și conectați primul dumneavoastră host Proxmox. Perioada de probă durează 14 zile și nu cere date de plată.