Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 package main | |
|
Vadim Sh.
2016/06/28 18:17:14
copyright
nodir
2016/06/28 22:08:57
Done.
| |
| 2 | |
| 3 import ( | |
| 4 "flag" | |
| 5 "fmt" | |
| 6 "net/http" | |
| 7 "os" | |
| 8 "strings" | |
| 9 "time" | |
| 10 | |
| 11 "github.com/luci/luci-go/common/api/buildbucket/buildbucket/v1" | |
| 12 ) | |
| 13 | |
| 14 var since = flag.Int64("since", 0, "analyze builds since this timestamp. Default s to 10 days ago.") | |
| 15 var bucket = flag.String("bucket", "", `buildbucket bucket name, e.g. "master.tr yserver.infra"`) | |
| 16 var builders = flag.String("builder", "", `comma-separated list of builder names without swarming suffix, e.g. "Infra Presubmit"`) | |
| 17 | |
| 18 const swarmingSuffix = " (Swarming)" | |
| 19 | |
| 20 func fetchBuilds(bucket, builder string, startingFrom time.Time) ([]*buildbucket .ApiBuildMessage, error) { | |
| 21 client, err := buildbucket.New(http.DefaultClient) | |
| 22 if err != nil { | |
| 23 return nil, err | |
| 24 } | |
| 25 client.BasePath = "https://cr-buildbucket.appspot.com/_ah/api/buildbucke t/v1/" | |
| 26 | |
| 27 req := client.Search() | |
| 28 req.Bucket(bucket) | |
| 29 req.Tag("builder:" + builder) | |
| 30 req.Status("COMPLETED") | |
| 31 req.MaxBuilds(100) | |
| 32 | |
| 33 var result []*buildbucket.ApiBuildMessage | |
| 34 for { | |
| 35 res, err := req.Do() | |
| 36 if err != nil { | |
| 37 return result, err | |
| 38 } | |
| 39 if res.Error != nil { | |
| 40 return result, fmt.Errorf(res.Error.Message) | |
| 41 } | |
| 42 | |
| 43 for _, b := range res.Builds { | |
| 44 createdAt := time.Unix(b.CreatedTs/1000000, 0) | |
| 45 if createdAt.Before(startingFrom) { | |
| 46 return result, nil | |
| 47 } | |
| 48 result = append(result, b) | |
| 49 } | |
| 50 | |
| 51 if len(res.Builds) == 0 || res.NextCursor == "" { | |
| 52 break | |
| 53 } | |
| 54 req.StartCursor(res.NextCursor) | |
| 55 } | |
| 56 return result, nil | |
| 57 } | |
| 58 | |
| 59 type buildSet struct { | |
| 60 builds []*buildbucket.ApiBuildMessage | |
| 61 bestResult string | |
| 62 } | |
| 63 | |
| 64 // groupBuilds groups builds by buildset tag. | |
| 65 func groupBuilds(builds []*buildbucket.ApiBuildMessage) map[string]*buildSet { | |
| 66 results := map[string]*buildSet{} | |
| 67 for _, b := range builds { | |
| 68 tags := parseTags(b.Tags) | |
| 69 buildSetName := tags["buildset"] | |
| 70 if buildSetName == "" { | |
| 71 fmt.Printf("skipped build %d: no buildset tag\n", b.Id) | |
| 72 continue | |
| 73 } | |
| 74 set := results[buildSetName] | |
| 75 if set == nil { | |
| 76 set = &buildSet{} | |
| 77 results[buildSetName] = set | |
| 78 } | |
| 79 | |
| 80 set.builds = append(set.builds, b) | |
| 81 if set.bestResult == "" || b.Result == "SUCCESS" { | |
| 82 set.bestResult = b.Result | |
| 83 } | |
| 84 } | |
| 85 return results | |
| 86 } | |
| 87 | |
| 88 func run() error { | |
| 89 flag.Parse() | |
| 90 if *bucket == "" { | |
| 91 return fmt.Errorf("bucket is not specified") | |
| 92 } | |
| 93 if *builders == "" { | |
| 94 return fmt.Errorf("builders are not specified") | |
| 95 } | |
| 96 if len(flag.Args()) > 0 { | |
| 97 return fmt.Errorf("unexpected arguments: %s", flag.Args()) | |
| 98 } | |
| 99 | |
| 100 var startingFrom time.Time | |
| 101 var duration time.Duration | |
| 102 if *since == 0 { | |
| 103 duration = 240 * time.Hour | |
| 104 startingFrom = time.Now().Add(-duration) | |
| 105 } else { | |
| 106 startingFrom = time.Unix(*since, 0) | |
| 107 duration = time.Since(startingFrom) | |
| 108 } | |
| 109 | |
| 110 for i, builder := range strings.Split(*builders, ",") { | |
| 111 builder = strings.TrimSpace(builder) | |
| 112 if builder == "" { | |
| 113 continue | |
| 114 } | |
| 115 | |
| 116 if i > 0 { | |
| 117 fmt.Println() | |
| 118 } | |
| 119 fmt.Printf("builder %q\n", builder) | |
| 120 fmt.Printf("searching for all builds since timestamp %d till %d. ..\n", | |
| 121 startingFrom.Unix(), time.Now().Unix()) | |
| 122 // We will actually fetch builds after after time.Now too, but i t is fine. | |
| 123 swarmingBuilds, err := fetchBuilds(*bucket, builder+swarmingSuff ix, startingFrom) | |
| 124 if err != nil { | |
| 125 return fmt.Errorf("could not fetch builds: %s", err) | |
| 126 } | |
| 127 if len(swarmingBuilds) == 0 { | |
| 128 fmt.Printf("no swarming builds for builder %q\n", builde r) | |
| 129 continue | |
| 130 } | |
| 131 buildbotBuilds, err := fetchBuilds(*bucket, builder, startingFro m) | |
| 132 if err != nil { | |
| 133 return fmt.Errorf("could not fetch builds: %s", err) | |
| 134 } | |
| 135 | |
| 136 swarmingBuildSets := groupBuilds(swarmingBuilds) | |
| 137 buildbotBuildSets := groupBuilds(buildbotBuilds) | |
| 138 | |
| 139 consistentN := 0 | |
| 140 inconsistentN := 0 | |
| 141 for setName, swarmingSet := range swarmingBuildSets { | |
| 142 buildbotSet := buildbotBuildSets[setName] | |
| 143 if buildbotSet == nil { | |
| 144 fmt.Printf("no buildbot builds for buildset %s\n ", setName) | |
| 145 continue | |
| 146 } | |
| 147 if buildbotSet.bestResult == swarmingSet.bestResult { | |
| 148 consistentN++ | |
| 149 continue | |
| 150 } | |
| 151 inconsistentN++ | |
| 152 | |
| 153 fmt.Printf("%s is inconsistent\n", setName) | |
| 154 for _, b := range swarmingSet.builds { | |
| 155 fmt.Printf(" %s %s\n", b.Result, b.Url) | |
| 156 } | |
| 157 for _, b := range buildbotSet.builds { | |
| 158 fmt.Printf(" %s %s\n", b.Result, b.Url) | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 fmt.Printf("%0.2f%% consistent build sets, %d buildbot builds, % d swarming builds\n", | |
| 163 100*float64(consistentN)/float64(consistentN+inconsisten tN), len(buildbotBuilds), len(swarmingBuilds)) | |
| 164 } | |
| 165 return nil | |
| 166 } | |
| 167 | |
| 168 func main() { | |
| 169 if err := run(); err != nil { | |
| 170 fmt.Fprintln(os.Stderr, err) | |
| 171 os.Exit(1) | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 func parseTags(tags []string) map[string]string { | |
| 176 result := make(map[string]string, len(tags)) | |
| 177 for _, t := range tags { | |
| 178 parts := strings.SplitN(t, ":", 2) | |
| 179 if len(parts) == 2 { | |
| 180 result[parts[0]] = parts[1] | |
| 181 } | |
| 182 } | |
| 183 return result | |
| 184 } | |
| OLD | NEW |