Padroneggiare la Jenkins Groovy Script Console per l'Amministrazione Avanzata del Sistema

Scopri il potere nascosto dell'amministrazione di Jenkins utilizzando la Groovy Script Console. Questa guida completa fornisce script Groovy di livello esperto e attuabili per amministratori di sistema per eseguire compiti complessi istantaneamente, come aggiornamenti di configurazione in blocco, gestione immediata degli agenti (disconnessione/riconnessione) e interruzione forzata dei build in esecuzione. Impara a interagire direttamente con il modello a oggetti di Jenkins per un'efficienza e capacità di risoluzione dei problemi senza pari.

Padroneggiare la Jenkins Groovy Script Console per l'Amministrazione Avanzata del Sistema

La Jenkins Groovy Script Console è utile per i lavori che non puoi eseguire in modo pulito dall'interfaccia utente: trovare un build bloccato, controllare lo stato dell'agente, ispezionare la configurazione del job o effettuare una modifica in blocco attentamente circoscritta. È anche uno dei modi più semplici per danneggiare un controller Jenkins se incolli uno script che non capisci.

Tratta la console come l'accesso root su un server di produzione. Leggi prima, stampa ciò che stai per modificare, testa su un controller non di produzione quando possibile, e solo dopo scrivi.


Comprendere la Jenkins Script Console

La Jenkins Script Console (Gestisci Jenkins -> Script Console) fornisce accesso diretto al modello a oggetti del controller Jenkins in esecuzione utilizzando Groovy. Puoi ispezionare job, build, nodi, viste, metadati delle credenziali, stato dei plugin e molti altri oggetti runtime.

Perché usare la Script Console?

  • Esecuzione Immediata: Esegui script istantaneamente senza attendere l'attivazione di un job o l'avvio di una pipeline.
  • Debug del Sistema: Accedi a stato interno, log e dettagli di configurazione non esposti tramite GUI.
  • Operazioni in Blocco: Modifica più job, riconfigura agenti o cancella dati vecchi su tutta l'istanza rapidamente.
  • Prototipazione di Script: Testa la logica Groovy prima di incorporarla in librerie condivise o pipeline dichiarative.

Precauzione di Sicurezza: Il Potere dell'Accesso Diretto

ATTENZIONE: Gli script eseguiti nella console vengono eseguiti con privilegi amministrativi completi sul master Jenkins. Uno script scritto male può corrompere configurazioni, cancellare build o crashare l'istanza Jenkins. Testa sempre gli script complessi a fondo prima in un ambiente non di produzione.


Oggetti Groovy Essenziali e Accesso all'API

Il potere della console deriva dall'accesso diretto agli oggetti core di Jenkins. Questi oggetti sono implicitamente disponibili nell'ambiente di esecuzione Groovy:

  • Jenkins.instance: L'oggetto singleton core di Jenkins, che rappresenta il controller in esecuzione.
  • Hudson: Un alias per Jenkins.
  • Jenkins.instance.getItemByFullName('NomeJob'): Accede a un job specifico.
  • Jenkins.instance.getComputer('NomeAgente'): Accede a un agente (nodo) specifico.

Accesso all'Istanza Jenkins

Per verificare di avere accesso, il comando più semplice è stampare la versione di Jenkins:

println "Versione Jenkins: ${Jenkins.instance.version}"
println "Esecuzione come utente: ${Jenkins.instance.getAuthentication().getName()}"

Nelle versioni recenti di Jenkins potresti vedere esempi che usano Jenkins.get() invece di Jenkins.instance. Entrambi i pattern appaiono negli script reali. Per nuovi script, Jenkins.get() è solitamente più chiaro:

import jenkins.model.Jenkins

def jenkins = Jenkins.get()
println "URL radice: ${jenkins.getRootUrl()}"

Script Amministrativi Pratici

Ecco diversi script attuabili che dimostrano il controllo amministrativo avanzato tramite la Script Console.

1. Aggiornamento delle Configurazioni dei Job in Blocco

Questo script itera attraverso i job Freestyle esistenti e aggiunge un suffisso alla descrizione. Nota la gestione null-safe; molti job non hanno descrizione.

import hudson.model.FreeStyleProject

final String SUFFISSO = " [Aggiornamento Automatico]"

def conteggio = 0

Jenkins.instance.getAllItems(FreeStyleProject.class).each { job ->
    def corrente = job.getDescription() ?: ""
    if (!corrente.endsWith(SUFFISSO)) {
        job.setDescription(corrente + SUFFISSO)
        job.save()
        println "Descrizione aggiornata per: ${job.getName()}"
        conteggio++
    }
}
println "\nFinito. Totale job aggiornati: ${conteggio}"

2. Gestione degli Agenti Jenkins (Nodi)

Gli amministratori spesso devono mettere gli agenti offline per manutenzione o disconnettere manualmente nodi malfunzionanti.

Disconnessione Temporanea di un Agente

Questo script disconnette un agente, impedendo l'avvio di nuovi build su di esso, ma permettendo il completamento dei build in esecuzione.

import hudson.model.Computer

final String NOME_AGENTE = "mio-agente-specifico"

def agente = Jenkins.get().getComputer(NOME_AGENTE)

if (agente) {
    // Imposta temporaneamente offline
    agente.setTemporarilyOffline(true, "Manutenzione avviata da Script Admin.")
    println "Agente '${NOME_AGENTE}' impostato temporaneamente offline."
} else {
    println "Agente '${NOME_AGENTE}' non trovato."
}

Forzare un Agente Offline e Disconnettere i Compiti in Esecuzione

Se un agente deve essere immediatamente spento, puoi forzarlo offline e disconnettere eventuali build in esecuzione, che verranno contrassegnati come falliti o abortiti a seconda della configurazione.

import hudson.model.Computer

final String NOME_AGENTE = "nodo-non-reattivo-01"

def agente = Jenkins.get().getComputer(NOME_AGENTE)

if (agente) {
    // Forza offline e disconnette immediatamente i compiti in esecuzione
    agente.doDoDisconnect()
    println "Agente '${NOME_AGENTE}' disconnesso forzatamente."
} else {
    println "Agente '${NOME_AGENTE}' non trovato."
}

3. Manipolazione dei Build in Esecuzione

Quando un build critico si blocca o necessita di cancellazione immediata, la Script Console fornisce il percorso più veloce.

Abortire un Build Specifico in Esecuzione

Per abortire un build identificato dal suo percorso completo (es. PipelineJob/NumeroBuild):

// Esempio: Abortire il build #5 del job chiamato 'DeployCritico'
final String NOME_JOB = "DeployCritico"
final int NUMERO_BUILD = 5

def job = Jenkins.get().getItemByFullName(NOME_JOB)

def build = job?.getBuild(NUMERO_BUILD)

if (build && build.isBuilding()) {
    build.doCancel()
    println "Build ${NOME_JOB}#${NUMERO_BUILD} è stato cancellato."
} else {
    println "Build ${NOME_JOB}#${NUMERO_BUILD} non è in esecuzione o non esiste."
}

4. Pulizia dei Vecchi Record di Build

La gestione dello spazio su disco spesso richiede la potatura aggressiva dei vecchi build. Questo script identifica e cancella tutti i build più vecchi di 30 giorni per un job specificato.

import hudson.model.Job
import java.util.concurrent.TimeUnit

final String JOB_TARGET = "JobArchiviazioneLegacy"
final int GIORNI_DA_MANTENERE = 30

def job = Jenkins.get().getItemByFullName(JOB_TARGET)

if (job instanceof Job) {
    long tempoLimite = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(GIORNI_DA_MANTENERE)
    int conteggioCancellati = 0

    job.getBuilds().each { build ->
        if (build.getTimeInMillis() < tempoLimite) {
            println "Cancellazione build vecchio: ${build.getDisplayName()}"
            build.delete()
            conteggioCancellati++
        }
    }
    println "\nPulizia completata. Cancellati ${conteggioCancellati} build per ${JOB_TARGET}."
} else {
    println "Job '${JOB_TARGET}' non trovato o non è un tipo di Job standard."
}

Migliori Pratiche per la Scrittura di Script nella Console

Quando si eseguono modifiche a livello di sistema, attenersi a queste migliori pratiche per mantenere la stabilità:

  1. Usa .save(): Ogni volta che modifichi un oggetto di configurazione (come un Job o una Vista), devi chiamare .save() su quell'oggetto affinché la modifica persista dopo il riavvio di Jenkins. Le configurazioni sono mantenute solo in memoria fino al salvataggio.
  2. Controlla l'Esistenza dell'Oggetto: Avvolgi sempre le chiamate API con controlli (if (oggetto) o try-catch) per evitare che la console si blocchi se scrivi male il nome di un job o agente.
  3. Evita Cicli Persistenti: Gli script vengono eseguiti in modo sincrono. Non eseguire cicli lunghi o processi direttamente nella console a meno che tu non sia certo che si completeranno rapidamente, poiché questo blocca l'interfaccia utente della console.
  4. Sfrutta i Metodi Integrati: Gli oggetti Groovy di Jenkins hanno spesso metodi helper specifici (come doCancel() o doDoDisconnect()). Usali invece di cercare di manipolare manualmente lo stato interno quando possibile.
  5. Usa la Modalità Silenziosa (se applicabile): Quando si eseguono operazioni in blocco che generano aggiornamenti eccessivi dello stato dei build, considera se disabilitare temporaneamente le funzionalità di notifica degli eventi sia giustificato, anche se questo di solito richiede un accesso più profondo al sistema rispetto all'amministrazione standard.

Pattern di Prova a Secco Più Sicuro

Per qualsiasi modifica in blocco, aggiungi prima un flag di prova a secco:

import jenkins.model.Jenkins
import hudson.model.Job

final boolean PROVA_A_SECCO = true
final String CORRISPONDENZA = "legacy-"

Jenkins.get().getAllItems(Job.class).findAll { job ->
    job.fullName.contains(CORRISPONDENZA)
}.each { job ->
    println "${PROVA_A_SECCO ? 'Aggiornerei' : 'Aggiornamento'} ${job.fullName}"

    if (!PROVA_A_SECCO) {
        job.setDescription((job.getDescription() ?: "") + "\nRevisionato durante la pulizia.")
        job.save()
    }
}

Eseguilo una volta con PROVA_A_SECCO = true, copia l'output nel tuo ticket di modifica, e solo dopo eseguilo con false. Questa piccola abitudine previene la maggior parte delle modifiche estese accidentali.

Leggere la Configurazione del Job Senza Modificarla

A volte la console è meglio usata come strumento di ricerca. Ad esempio, per trovare job Pipeline che fanno ancora riferimento a un vecchio host Git:

import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.job.WorkflowJob

final String RICERCA = "git.old.example.com"

Jenkins.get().getAllItems(WorkflowJob.class).each { job ->
    def definizione = job.getDefinition()
    def testo = definizione?.getScript()
    if (testo?.contains(RICERCA)) {
        println "Trovato ${RICERCA} in ${job.fullName}"
    }
}

Questo esempio funziona solo per script Pipeline inline. Se il job usa Jenkinsfile da SCM, Jenkins memorizza la definizione SCM piuttosto che il contenuto del file. Questa distinzione è importante: la console può ispezionare la configurazione di Jenkins, ma non può leggere magicamente ogni ramo di ogni repository remoto a meno che il tuo script non lo faccia esplicitamente.

Trovare Build Bloccati Senza Indovinare

Durante un incidente, la prima domanda è spesso "cosa sta girando in questo momento?" Questo script stampa i build in esecuzione con la loro durata e executor:

import jenkins.model.Jenkins

Jenkins.get().getComputers().each { computer ->
    computer.executors.each { executor ->
        def eseguibile = executor.currentExecutable
        if (eseguibile) {
            def build = eseguibile
            println "${computer.displayName} :: ${build.fullDisplayName} :: ${build.durationString}"
        }
    }
}

Usalo prima come script di ispezione. Se devi abortire qualcosa, prendi di mira un build noto piuttosto che cancellare tutto ciò che sembra vecchio. Migrazioni di database di lunga durata, job di rilascio e pipeline di approvazione manuale possono sembrare "bloccati" dall'esterno.

Per i job Pipeline, puoi anche ispezionare se un build è in pausa per input:

import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.jenkinsci.plugins.workflow.support.steps.input.InputAction

Jenkins.get().getAllItems().each { job ->
    job.builds?.findAll { it instanceof WorkflowRun && it.isBuilding() }?.each { esecuzione ->
        def input = esecuzione.getAction(InputAction)
        if (input) {
            println "In attesa di input: ${esecuzione.fullDisplayName}"
        }
    }
}

Questo previene un errore comune: abortire un deployment che sta intenzionalmente aspettando approvazione.

Ispezione di Plugin e Versioni

La console è utile quando l'interfaccia utente è lenta o hai bisogno di un inventario rapido. Questo stampa i plugin installati e le versioni:

import jenkins.model.Jenkins

Jenkins.get().pluginManager.plugins
    .sort { it.shortName }
    .each { plugin ->
        println "${plugin.shortName}:${plugin.version}"
    }

Non usare la Script Console come sostituto di un processo di aggiornamento plugin gestito. Gli aggiornamenti dei plugin possono influenzare il caricamento dei job, il comportamento delle Pipeline, i binding delle credenziali e le connessioni degli agenti. La console è migliore per ispezione, diagnosi di emergenza o piccole riparazioni mirate.

Esegui il Backup Prima di Script di Scrittura

Prima di eseguire qualsiasi script che chiami .save(), cancelli build, disconnetta agenti o modifichi definizioni di job, assicurati di avere un backup corrente di $JENKINS_HOME o della fonte di configurazione gestita del tuo controller. Se la tua istanza Jenkins è configurata da JCasC, Job DSL, valori Helm o un altro sistema basato su Git, ricorda che una modifica dalla console potrebbe essere sovrascritta dalla successiva riconciliazione.

In quegli ambienti, usa la console per confermare il problema, poi correggi la fonte di verità. Una correzione solo dalla console è accettabile per un'emergenza, ma registrala in modo che la configurazione durevole possa essere aggiornata successivamente.

Accesso Remoto alla Script Console

Molti amministratori conoscono la console del browser, ma Jenkins può anche eseguire Groovy tramite CLI quando questo accesso è abilitato e consentito:

java -jar jenkins-cli.jar -s https://jenkins.example.com/ groovy = < script.groovy

Ciò è utile per script revisionati perché puoi mantenere il file Groovy nel controllo versione, eseguirlo tramite revisione tra pari ed eseguire il contenuto esatto che è stato approvato. Rende anche più facile catturare l'output in un ticket di incidente.

Non abilitare CLI o esecuzione remota di script con leggerezza. Il permesso richiesto per l'accesso alla Script Console è altamente privilegiato. Limitalo ad amministratori fidati, usa la registrazione di audit dove disponibile e preferisci sessioni amministrative di breve durata. Se la tua organizzazione utilizza il controllo degli accessi basato sui ruoli, verifica chi ha effettivamente Overall/Administer o diritti equivalenti prima di presumere che la console sia bloccata.

Per manutenzione ripetibile, un job Jenkins che esegue uno script revisionato sotto parametri controllati è spesso meglio del lavoro ad hoc dalla console del browser. La console rimane lo strumento di emergenza; l'automazione controllata da versione dovrebbe gestire i compiti che prevedi di ripetere.

Prima di eseguire uno script remoto, stampa l'URL di Jenkins e il nome di autenticazione corrente all'inizio dell'output. Sembra banale, ma cattura l'errore peggiore: eseguire una riparazione di produzione contro il controller sbagliato o sotto l'account sbagliato.

import jenkins.model.Jenkins

def j = Jenkins.get()
println "Controller: ${j.getRootUrl()}"
println "Utente: ${j.getAuthentication().getName()}"

Cosa Non Mettere nella Console

Evita script che dormono per molto tempo, fanno polling all'infinito, scaricano grandi file remoti o eseguono cancellazioni estese di file system. La console viene eseguita all'interno del processo del controller. Se lo script consuma CPU, blocca thread o riempie la memoria, influisce sul sistema CI stesso.

Evita anche di stampare segreti. Gli oggetti delle credenziali di Jenkins sono intenzionalmente protetti, ma gli amministratori possono comunque scrivere script che espongono materiale sensibile. Se devi controllare le credenziali, stampa ID, descrizioni, domini e riferimenti di utilizzo. Non stampare valori segreti nel browser, nei log di build o nella chat.

I migliori script della console sono brevi, noiosi e reversibili. Usali per ispezionare lo stato, eseguire una riparazione mirata o automatizzare un compito amministrativo noto. Quando uno script diventa abbastanza lungo da aver bisogno di test, spostalo in un repository di amministrazione condiviso o in un job di gestione Jenkins dove può essere revisionato come codice normale.