Created
April 12, 2018 11:54
-
-
Save jhinrichsen/814115aff65c5dc9fe933c57c4cc37c4 to your computer and use it in GitHub Desktop.
Jenkins: Show average duration of all builds for a given freestyle job
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Show average duration of all builds for a given Jenkins freestyle job | |
// return codes: | |
// 1: bad usage | |
package main | |
import ( | |
"encoding/json" | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"net/url" | |
"os" | |
"path" | |
"strings" | |
"time" | |
) | |
// JSON encoding does not do the allBuilds>url trick => nested structs | |
type JobResponse struct { | |
// FreeStyleProject `json:"freeStyleProject"` | |
Class string `json:"_class"` | |
AllBuilds []Build `json:"allBuilds"` | |
} | |
type Build struct { | |
DurationMs int `json:"duration"` | |
Id string `json:"id"` // Is a number but represented as string | |
Url string `json:"url"` | |
Result string `json:"result"` | |
} | |
func (a Build) Duration() time.Duration { | |
// Jenkins duration is in milliseconds | |
d := time.Duration(a.DurationMs) * time.Millisecond | |
return d | |
} | |
func die(err error) { | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
// Builds converts a JSON structure into a list of Builds | |
func Builds(buf []byte) []Build { | |
var e JobResponse | |
err := json.Unmarshal(buf, &e) | |
die(err) | |
// log.Printf("unmarshal: %+v\n", e) | |
return e.AllBuilds | |
} | |
// Filter out broken builds, only consider SUCCESS and UNSTABLE | |
func Filter(b Build) bool { | |
return b.Result == "FAILURE" | |
} | |
// FetchBuilds returns Jenkins JSON 'allBuilds' structure, including duration, | |
// id, result, and url | |
func FetchBuilds(jenkins string, job string) []byte { | |
var sb strings.Builder | |
// path.Join() cannot be used for URLs | |
// base := path.Join(jenkins, "job", job, "api", "json") | |
u, err := url.Parse(jenkins) | |
die(err) | |
rel, _ := url.Parse(path.Join("job", job, "api", "json")) | |
sb.WriteString(u.ResolveReference(rel).String()) | |
sb.WriteString("?tree=allBuilds[duration,id,result,url]&pretty=true") | |
s := sb.String() | |
log.Printf("requesting %s\n", s) | |
res, err := http.Get(s) | |
die(err) | |
defer res.Body.Close() | |
buf, err := ioutil.ReadAll(res.Body) | |
log.Printf("buf: %s\n", buf) | |
die(err) | |
return buf | |
} | |
// Show presents some statistics for given job. | |
func Show(job string, n int, total time.Duration) { | |
d := time.Duration(total.Nanoseconds() / int64(n)) | |
fmt.Printf("job: %s, %d succesful builds, average: %v\n", | |
job, n, d) | |
} | |
func main() { | |
flag.Usage = func() { | |
fmt.Fprintf(os.Stderr, "Usage: %s <job> ...\n", os.Args[0]) | |
flag.PrintDefaults() | |
} | |
jenkins := flag.String("jenkins", "http://localhost:8080/jenkins", | |
"fully qualified URL for Jenkins instance") | |
flag.Parse() | |
// Arguments are job names | |
if len(flag.Args()) == 0 { | |
flag.Usage() | |
os.Exit(1) | |
} | |
for _, job := range flag.Args() { | |
log.Printf("Processing job %s\n", job) | |
jsonBuf := FetchBuilds(*jenkins, job) | |
bs := Builds(jsonBuf) | |
sum := time.Duration(0) | |
n := 0 | |
for _, b := range bs { | |
if Filter(b) { | |
continue | |
} | |
n++ | |
sum = time.Duration(sum.Nanoseconds() + | |
b.Duration().Nanoseconds()) | |
} | |
Show(job, n, sum) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment