Tech

Test-Driven Infrastructure: quando i test escono dal codice applicativo

devopsinfrastrutturatestingansibletdd

Il 3 luglio 2019, al Vodafone Theatre di Milano, per la prima edizione della Working Software Conference ho fatto qualcosa che all’epoca mi sembrava un azzardo: un live-coding in cui scrivevo test prima dei ruoli Ansible. Lo replicai qualche mese dopo ad Agile Venture Firenze. Questo articolo non è la trascrizione di quel talk — le slide e il video per quello bastano — ma quello che mi è rimasto addosso dopo averlo preparato, portato in scena e discusso con chi era in sala.

Per quasi dieci anni ho fatto il sistemista prima di tornare a scrivere codice: il muro tra dev e ops l’ho abitato da entrambe le parti. Una delle cose che mi porto dietro è che l’infrastruttura merita gli stessi test del codice applicativo — anzi, è lì che i test mancano quasi sempre.

Automatizzare non basta

Si parla spesso di Pipeline as Code e Infrastructure as Code, ma troppo spesso la pratica si ferma a prendere gli script di configurazione e deploy e salvarli in un repository. Non è questo che li rende codice.

Quando scriviamo template Jinja2, definiamo variabili in YAML e componiamo playbook dichiarativi, stiamo a tutti gli effetti scrivendo software con un DSL. Gli utenti finali di quel software siamo noi: gli sviluppatori e le persone di operations che lo eseguono e lo mantengono. E se è software, vale tutto quello che sappiamo sul software: branching model, linter e analisi statica, test automatici, refactoring — e sì, anche il TDD.

La parte difficile non è tecnica: è cambiare il modo in cui consideriamo quelle automazioni. Finché le trattiamo come script usa-e-getta, nessuno investirà mai un minuto per testarle.

Testare il sistema, non lo strumento

C’è un errore che vedevo allora e vedo ancora oggi: scrivere test che verificano Ansible invece del sistema. Un test che controlla “il task è stato eseguito” non ci dice nulla; un test che controlla “il servizio risponde sulla porta 80” ci dice se il sistema fa quello che deve.

Nel live-coding l’obiettivo era volutamente banale: installare e configurare uno stack LAMP in modo semplice, affidabile e ripetibile, su CentOS 7 e Ubuntu 18.04. Gli strumenti: Molecule per orchestrare lo scenario di test, Testinfra e pytest per scrivere le asserzioni, Docker per avere ambienti usa-e-getta. Un test Testinfra somiglia a questo:

def test_apache_is_installed(host):
    assert host.package("httpd").is_installed


def test_apache_running_and_enabled(host):
    service = host.service("httpd")
    assert service.is_running
    assert service.is_enabled

Niente di sofisticato — ed è proprio il punto. Il ciclo era quello classico del TDD: scrivi un test che fallisce, scrivi il minimo di playbook per farlo passare, fai refactoring in sicurezza. Molecule rendeva il ciclo abbastanza veloce da poterlo fare dal vivo, davanti a una platea, senza diventare una lezione di pazienza.

Cosa ho imparato facendolo dal vivo

Fare TDD su un’applicazione davanti a un pubblico è rischioso; farlo su infrastruttura, con container che si creano e distruggono in tempo reale, lo è ancora di più. Proprio per questo il live-coding mi ha insegnato più del talk stesso:

  • Il ciclo di feedback dev’essere corto. Se lanciare i test richiede dieci minuti, non farai TDD: farai test ogni tanto, quando avanza tempo. La velocità del ciclo è una proprietà del design, non un dettaglio di ottimizzazione.
  • Gli ambienti di test devono essere usa-e-getta. Un test che dipende dallo stato lasciato dall’esecuzione precedente è una bugia in attesa di essere scoperta. Docker come driver di Molecule non era una scelta estetica: era ciò che rendeva i test affidabili.
  • Testare su più distribuzioni cambia il design. Scrivere lo stesso ruolo per CentOS e Ubuntu mi ha obbligato a isolare le differenze invece di spargerle nei task. I test multi-piattaforma funzionano come una forzatura verso un design più pulito — esattamente come i test unitari fanno con il codice applicativo.

Errori che vedo ancora oggi

Gli strumenti sono cambiati — Molecule, Testinfra e persino Ansible non sono più quelli del 2019 — ma gli errori concettuali che raccontavo nel talk li ritrovo intatti nei progetti in cui entro:

  • Confondere IaC con “gli script sono in git”. Un repository pieno di playbook mai testati non è Infrastructure as Code: è debito tecnico con il controllo di versione.
  • Scrivere i test dopo, se avanza tempo. Non avanza mai tempo. I test scritti a posteriori verificano quello che il codice fa, non quello che dovrebbe fare, e perdono tutto il valore di design del TDD.
  • Dipendere da una macchina specifica. “Sul server di CI funziona” è la versione infrastructure di “sul mio portatile funziona”. Se l’ambiente di test non è riproducibile, il test non è un test.
  • Rassegnarsi al manuale. Il paradosso che citavo in chiusura del talk è ancora vero: un’automazione non testata mostra comportamenti aleatori, e la manutenzione diventa così costosa che fare le cose a mano finisce per sembrare la soluzione migliore. Non lo è: è il sintomo del problema.

Quello che conta davvero

Adottare il TDD sull’infrastruttura non richiede un grande dispendio di tempo: richiede un investimento mentale. Lo sforzo maggiore è smettere di considerare le automazioni un sottoprodotto e iniziare a trattarle come il software che sono, con utenti, obiettivi di valore e standard di qualità. Fatto quel passo, gli strumenti sono quasi un dettaglio — quelli giusti si trovano, e cambieranno ancora.

Il risultato, e qui il 2019 non mi ha smentito, è una qualità di pipeline e automazioni di qualche ordine di grandezza superiore: rilasci ripetibili, modifiche senza terrore, onboarding che non si basa sulla memoria di chi ha scritto gli script.

Approfondimenti

Ne ho parlato dal vivo nel live-coding Test-Driven Infrastructure with Ansible: nella pagina del talk trovi il video registrato a Milano e il repository con il codice dello stack LAMP costruito live, sul palco per ben 2 volte!

Il metodo dietro il codice

Le pratiche tecniche funzionano quando aiutano il team a lavorare con più autonomia.

Scopri il metodo