Jenkins - Conteggio build per stato

In questo articolo vorrei esporre la mia idea su come, dato un elenco di build di un certo job, da jenkins leggere il numero di pipeline in esecuzione o nei diversi stati.

 

Per effettuare una chiamata REST a jenkins e recuperarne le informazioni è stato spiegato in questo articolo . In questo caso si tratta di eseguire una chiamata del tipo: 

<JENKINS_URL>/job/<SOMEPIPELINE>/api/json?tree=allBuilds[id,timestamp,duration,result]&pretty=true

il cui endpoint è quello che ho voluto evidenziare in grassetto.

 

Questo tipo di chiamata restituisce una risposta come questa:

 

{

  "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowJob",

  "allBuilds" : [

{

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 837491,

      "id" : "46671",

  "result" : null,

      "timestamp" : 1647202842314

    },

    {

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 991915,

      "id" : "46670",

  "result" : "SUCCESS",

      "timestamp" : 1647195253529

    },

{

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 678418,

      "id" : "46669",

      "result" : "UNSTABLE",

      "timestamp" : 1648292021523

    },

    {

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 646110,

      "id" : "46668",

      "result" : "FAILURE",

      "timestamp" : 1648290252397

    },

    {

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 615499,

      "id" : "46667",

      "result" : "ABORTED",

      "timestamp" : 1648290014257

    },

    {

      "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",

      "duration" : 538937,

      "id" : "46666",

      "result" : "ABORTED",

      "timestamp" : 1648288902869

    }

  ]

}

 

Analizzando il json possiamo capire che è formato da diversi record che contengono le informazioni che sono state richieste più una informazione aggiuntiva iniziale che poco ci interessa allo scopo.

 

Sappiamo che se la duration è pari a 0 significa che la build è ancora in corso; in questo caso abbiamo il valore "null" come risultato in quanto non è terminata la pipeline. In tutti gli altri casi possiamo conteggiare i diversi record in base al risultato.

 

Dobbiamo quindi capire come leggere il testo json con vbscript. Non esiste una funzione nativa in vbscript per leggere un json quindi dobbiamo ideare qualche metodo.

 

Possiamo intanto vedere che la struttura è ben definita, rigida e quindi ci torna utile. Vediamo ad esempio che, quelli che definisco record (possono essere visti anche come oggetti) sono racchiusi da una parentesi "[" aperta e una parentesi "]" chiusa, poi, all'interno di questo contesto, i singoli elementi sono racchiusi da parentesi "{" aperta e parentesi "}" chiusa. Possiamo quindi provare a sfruttare le regular expression, in particolare questi due pattern:

 

FirstPattern = "\[[\s\S]*?\]"         <-- significa trova parentesi "[" aperta e la PRIMA parentesi "]" chiusa

SecondPattern= "\{[\w\W]*?\}" <-- stessa cosa ma per le parentesi "{ }"

 

In questa soluzione ho usato anche una classe per gestire le build. Ha un solo membro, un array multidimensionale di 4 righe ed colonne.

 

Nel codice ho riportato il testo json, di cui sopra, in una variabile stringa. Eccolo:

 

 

'Class to manage all the element of the json file returns from the request to jenkins

Class BuildManager

Private theAllBuildsTag()

 

'Getter

Public Property Get allBld()

        allBld = theAllBuildsTag

    End Property

    

'Setter

    Public Property Let allBld(data)

Dim arrData : arrData = split(data,"|")

Dim Lim1 : Lim1 = Ubound(theAllBuildsTag,2)

 

theAllBuildsTag(0, Lim1) = arrData(0)

theAllBuildsTag(1, Lim1) = arrData(1)

theAllBuildsTag(2, Lim1) = arrData(2)

theAllBuildsTag(3, Lim1) = arrData(3)

Lim1 = Lim1 + 1

 

'Using Preserve option it is possible to modify only last dimension of the multidimensional array

Redim Preserve theAllBuildsTag(3, Lim1)

 

'The matrix will be 4 x buildnumber with:

'at position 0:  the Duration of all the builds

'at position 1:   all the IDs

'at position 2: the Results of all the builds

'at position 3: the Timestamp of all the builds

 

'0,0 - 0,1 - 0,2 - 0,3 ... 0,n = Duration

'1,0 - 1,1 - 1,2 - 1,3 ... 1,n = IDs

'2,0 - 2,1 - 2,2 - 2,3 ... 2,n = Results

'3,0 - 3,1 - 3,2 - 3,3 ... 3,n = Timestamps

 

'so data of the single build must be read in vertical direction not in horizontal direction

 

    End Property

 

    ' Parameterless Constructor

    Public Sub Class_Initialize()

Redim theAllBuildsTag(3, 0)

    End Sub

 

Public Sub Class_Terminate()

End Sub

 

'Function to retrieve the number of Running builds

Public Function NRunningBuild()

Dim intRes : intRes = 0

for i = 0 to Ubound(theAllBuildsTag,2) - 1

 

if theAllBuildsTag(0,i) = "0" then

intRes = intRes + 1

end if

 

next

NRunningBuild = intRes

End Function

 

'Function to retrieve the number of all the build in the state passed as arguments. For Example "SUCCESS", "FAILURE", "ABORTED", etc...

Public Function NStateBuild(state)

Dim intRes : intRes = 0

for i = 0 to Ubound(theAllBuildsTag,2) - 1

if theAllBuildsTag(2,i) = chr(34) & state & chr(34) then

intRes = intRes + 1

end if

next

NStateBuild = intRes

End Function

 

End Class

 

'******************************************************************************************************

'The simulation of a jenkins response and store it in a string variable

'strInp is for a test - it simulate a Jenkins response of:

'<JENKINS_URL>/job/<SOMEPIPELINE>/api/json?tree=allBuilds[id,timestamp,duration,result]&pretty=true

'******************************************************************************************************

dim strInp

strInp = "{" & chr(34) & _

  "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowJob" & chr(34) & "," & chr(34) & _

  "allBuilds" & chr(34) & " : [ " & _

"{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration" & chr(34) & " : 0," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46671" & chr(34) & "," & _

  chr(34) & "result" & chr(34) & " : null," & _ 

      chr(34) & "timestamp" & chr(34) & " : 1647202842314" & _

    "}," & _

    "{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration"  & chr(34) & " : 991915," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46670" & chr(34) & "," & _ 

  chr(34) & "result" & chr(34) & " : " & chr(34) & "SUCCESS" & chr(34) & "," & _ 

      chr(34) & "timestamp" & chr(34) & " : 1647195253529" & _

    "}," & _

"{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration"  & chr(34) & " : 678418," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46669" & chr(34) & "," & _ 

      chr(34) & "result" & chr(34) & " : " & chr(34) & "UNSTABLE" & chr(34) & "," & _ 

      chr(34) & "timestamp" & chr(34) & " : 1648292021523" & _

    "}," & _

"{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration"  & chr(34) & " : 646110," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46668" & chr(34) & "," & _ 

      chr(34) & "result" & chr(34) & " : " & chr(34) & "FAILURE" & chr(34) & "," & _

      chr(34) & "timestamp" & chr(34) & " : 1648290252397" & _

    "}," & _

"{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration"  & chr(34) & " : 615499," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46667" & chr(34) & "," & _

      chr(34) & "result" & chr(34) & " : " & chr(34) & "ABORTED" & chr(34) & "," & _

      chr(34) & "timestamp" & chr(34) & " : 1648290014257" & _

    "}," & _

"{" & _

      chr(34) & "_class" & chr(34) & " : " & chr(34) & "org.jenkinsci.plugins.workflow.job.WorkflowRun" & chr(34) & "," & _

      chr(34) & "duration"  & chr(34) & " : 538937," & _

      chr(34) & "id" & chr(34) & " : " & chr(34) & "46666" & chr(34) & "," & _

      chr(34) & "result" & chr(34) & " : " & chr(34) & "ABORTED" & chr(34) & "," & _

      chr(34) & "timestamp" & chr(34) & " : 1648288902869" & _

    "}" & _

  "]" & _

"}"

'******************************************************************************************************

' here is the MAIN that is only a series of call to the AnalyzeBuild sub 

' to returns different number of builds filter by status

'******************************************************************************************************

AnalyzeBuild strInp, "RUNNING"

AnalyzeBuild strInp, "SUCCESS"

AnalyzeBuild strInp, "UNSTABLE"

AnalyzeBuild strInp, "FAILURE"

AnalyzeBuild strInp, "ABORTED"

' end of the MAIN

'******************************************************************************************************

 

'******************************************************************************************************

' the SUB that do the job

'******************************************************************************************************

Sub AnalyzeBuild(inpJson, state)

 

dim FirstPattern : FirstPattern = "\[[\s\S]*?\]"

dim SecondPattern: SecondPattern= "\{[\w\W]*?\}"

dim regEx : set regEx = CreateObject("VBScript.RegExp")

dim regEx2 : set regEx2 = CreateObject("VBScript.RegExp")

dim myRec, arrElements, dur, idR, resR, tmstmpR

dim oBM

with regEx

.Pattern = FirstPattern

.IgnoreCase = True

.Global = True

end with

 

if regEx.Test(inpJson) then

set colMatch = regEx.Execute(inpJson)

if colMatch.Count > 0 then

with regEx2

.Pattern = SecondPattern

.IgnoreCase = true

.Global = true

end with

if regEx2.Test(colMatch.Item(0).Value) then

set oBM = new BuildManager

set mtch2 = regEx2.Execute(colMatch.Item(0).Value)

if mtch2.Count > 0 then

for j=0 to mtch2.Count-1

myRec = mtch2.Item(j).Value

arrElements = split(myRec, ",")

 

dur = split(arrElements(1)," : ")(1)

idR = split(arrElements(2)," : ")(1)

resR = split(arrElements(3)," : ")(1)

tmstmpR = left(split(arrElements(4)," : ")(1), len(split(arrElements(4)," : ")(1)) - 1)

 

oBM.allBld = dur & "|" & idR & "|" & resR & "|" & tmstmpR

 

next

end if

end if

end if

end if

 

Select Case state

case "RUNNING":

msgbox "The number of RUNNING builds are: " & oBM.NRunningBuild

 

case else:

msgbox "The number of " & state & " builds are: " & oBM.NStateBuild(state)

 

End Select

 

End Sub

'****************************************************************************************************** 

 

Questo codice può essere copiato, salvato come file vbs ed eseguito. Verranno visualizzate diversi popup che indicano il numero di build nei diversi stati.

 

 

Pag: <<   <