Skip to content

Instantly share code, notes, and snippets.

@arxcruz
Created May 28, 2020 11:47
Show Gist options
  • Save arxcruz/6121af45bbc7b8438db656f5ac883d73 to your computer and use it in GitHub Desktop.
Save arxcruz/6121af45bbc7b8438db656f5ac883d73 to your computer and use it in GitHub Desktop.
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"github.com/jedib0t/go-pretty/table"
"gopkg.in/yaml.v2"
)
// SkipTests - Struct returned by the yaml file containing the skipped
// tests
type SkipTests struct {
KnownFailures []struct {
Test string `yaml:"test"`
} `yaml:"known_failures"`
}
// ListJobs is the struct to return the list of jobs you want to check
type ListJobs []string
func (i *ListJobs) String() string {
return "List of jobs"
}
// Set the value parsed by the flags parser
func (i *ListJobs) Set(value string) error {
*i = append(*i, value)
return nil
}
// Builds - Basic struct returned by zuul api in json format
// We don't need all the values, just these two,
// Status and the URL
type Builds struct {
Status string `json:"result"`
URL string `json:"log_url"`
}
// TestResult - This is the result of the test, wether is
// failing or passing, it acts as a counter
type TestResult struct {
Passing int
Failing int
}
var jobs ListJobs
var yamlFile string
var showAllTests bool
func parseArgs() {
// Default job values
jobs = ListJobs{
"periodic-tripleo-ci-centos-8-standalone-full-tempest-scenario-master",
"periodic-tripleo-ci-centos-8-standalone-full-tempest-api-master"}
flag.Var(&jobs, "job", "List of jobs to collect tempest result")
flag.StringVar(&yamlFile, "file", "skiplist.yaml", "Skip list yaml file")
flag.BoolVar(&showAllTests, "showtests", false, "Show all tests")
flag.Parse()
}
func main() {
parseArgs()
tests := make(map[string]*TestResult)
lastTen := make(map[string]*TestResult)
builds := make(map[string][]Builds)
var sumBuilds, lastTenBuilds []Builds
skipped, err := loadYaml(yamlFile)
if err != nil {
log.Fatalf("Failing in load YAML file: %v", err)
}
for _, job := range jobs {
buildsJob := getBuilds(job)
builds[job] = buildsJob
lastTenBuilds = append(lastTenBuilds, buildsJob[:10]...)
sumBuilds = append(sumBuilds, buildsJob...)
}
collectData(sumBuilds, tests)
collectData(lastTenBuilds, lastTen)
report(tests, lastTen, skipped)
}
func loadYaml(yamlPath string) (*SkipTests, error) {
skipTests := &SkipTests{}
file, err := os.Open(yamlPath)
if err != nil {
return nil, err
}
defer file.Close()
d := yaml.NewDecoder(file)
if err := d.Decode(&skipTests); err != nil {
return nil, err
}
return skipTests, nil
}
func collectData(builds []Builds, tests map[string]*TestResult) {
ch := make(chan string)
size := 0
for _, build := range builds {
if build.Status != "SKIPPED" {
go fetchLogs(build.URL, ch)
size++
}
}
for i := 0; i < size; i++ {
results := parseResults(ch)
for _, result := range results {
if tests[result[0]] == nil {
tests[result[0]] = &TestResult{}
}
if result[1] == "FAILED" {
tests[result[0]].Failing++
} else {
tests[result[0]].Passing++
}
}
}
}
func report(results, lastTen map[string]*TestResult, skipped *SkipTests) {
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Test name", "Passing", "Failing", "Last 10 passing", "Last 10 failing"})
if showAllTests {
for key, value := range results {
if lastTen[key] == nil {
lastTen[key] = &TestResult{}
}
t.AppendRow([]interface{}{key, value.Passing, value.Failing, lastTen[key].Passing, lastTen[key].Failing})
}
} else {
for _, failure := range skipped.KnownFailures {
if results[failure.Test] != nil {
t.AppendRow([]interface{}{failure.Test, results[failure.Test].Passing, results[failure.Test].Failing, lastTen[failure.Test].Passing, lastTen[failure.Test].Failing})
}
}
}
t.Render()
}
func fetchLogs(url string, ch chan<- string) {
path := "logs/undercloud/var/log/tempest/tempest_run.log.txt.gz"
fullURL := fmt.Sprintf("%s%s", url, path)
// log.Println("Downloading ", fullURL)
response, err := http.Get(fullURL)
if err != nil {
ch <- fmt.Sprintf("Error in get %s: %v", fullURL, err)
return
}
data, err := ioutil.ReadAll(response.Body)
if err != nil {
ch <- fmt.Sprintf("Error while reading data from %s: %v", fullURL, err)
return
}
ch <- string(data)
}
func getBuilds(jobName string) []Builds {
var url = "https://review.rdoproject.org/zuul/api/builds?job_name=%s"
var builds []Builds
response, err := http.Get(fmt.Sprintf(url, jobName))
if err != nil {
log.Fatalf("The HTTP request failed with error %s\n", err)
} else {
data, _ := ioutil.ReadAll(response.Body)
if err := json.Unmarshal(data, &builds); err != nil {
log.Printf("The Unmarshal failed with error %s\n", err)
}
}
return builds
}
func parseResults(ch chan string) [][2]string {
r, _ := regexp.Compile(`\{\d\}\s(setUpClass\s)?\(?(?P<test>[^\(].*[^\)])(\))?\s\[\d.*\.\d.*\]\s...\s(?P<status>ok|FAILED)`)
var result [][2]string
for _, matches := range r.FindAllStringSubmatch(<-ch, -1) {
result = append(result, [2]string{matches[2], matches[4]})
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment