Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Unified Diff: go/src/infra/tools/swarming-transition/cmd/swarmbucketcheck/main.go

Issue 2110453002: add swarmbucketcheck (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: coverage Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: go/src/infra/tools/swarming-transition/cmd/swarmbucketcheck/main.go
diff --git a/go/src/infra/tools/swarming-transition/cmd/swarmbucketcheck/main.go b/go/src/infra/tools/swarming-transition/cmd/swarmbucketcheck/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..12bf0cef725248c1519013fbb2664dd5e1533ee6
--- /dev/null
+++ b/go/src/infra/tools/swarming-transition/cmd/swarmbucketcheck/main.go
@@ -0,0 +1,188 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/luci/luci-go/common/api/buildbucket/buildbucket/v1"
+)
+
+var since = flag.Int64("since", 0, "analyze builds since this timestamp. Defaults to 10 days ago.")
+var bucket = flag.String("bucket", "", `buildbucket bucket name, e.g. "master.tryserver.infra"`)
+var builders = flag.String("builder", "", `comma-separated list of builder names without swarming suffix, e.g. "Infra Presubmit"`)
+
+const swarmingSuffix = " (Swarming)"
+
+func fetchBuilds(bucket, builder string, startingFrom time.Time) ([]*buildbucket.ApiBuildMessage, error) {
+ client, err := buildbucket.New(http.DefaultClient)
+ if err != nil {
+ return nil, err
+ }
+ client.BasePath = "https://cr-buildbucket.appspot.com/_ah/api/buildbucket/v1/"
+
+ req := client.Search()
+ req.Bucket(bucket)
+ req.Tag("builder:" + builder)
+ req.Status("COMPLETED")
+ req.MaxBuilds(100)
+
+ var result []*buildbucket.ApiBuildMessage
+ for {
+ res, err := req.Do()
+ if err != nil {
+ return result, err
+ }
+ if res.Error != nil {
+ return result, fmt.Errorf(res.Error.Message)
+ }
+
+ for _, b := range res.Builds {
+ createdAt := time.Unix(b.CreatedTs/1000000, 0)
+ if createdAt.Before(startingFrom) {
+ return result, nil
+ }
+ result = append(result, b)
+ }
+
+ if len(res.Builds) == 0 || res.NextCursor == "" {
+ break
+ }
+ req.StartCursor(res.NextCursor)
+ }
+ return result, nil
+}
+
+type buildSet struct {
+ builds []*buildbucket.ApiBuildMessage
+ bestResult string
+}
+
+// groupBuilds groups builds by buildset tag.
+func groupBuilds(builds []*buildbucket.ApiBuildMessage) map[string]*buildSet {
+ results := map[string]*buildSet{}
+ for _, b := range builds {
+ tags := parseTags(b.Tags)
+ buildSetName := tags["buildset"]
+ if buildSetName == "" {
+ fmt.Printf("skipped build %d: no buildset tag\n", b.Id)
+ continue
+ }
+ set := results[buildSetName]
+ if set == nil {
+ set = &buildSet{}
+ results[buildSetName] = set
+ }
+
+ set.builds = append(set.builds, b)
+ if set.bestResult == "" || b.Result == "SUCCESS" {
+ set.bestResult = b.Result
+ }
+ }
+ return results
+}
+
+func run() error {
+ flag.Parse()
+ if *bucket == "" {
+ return fmt.Errorf("bucket is not specified")
+ }
+ if *builders == "" {
+ return fmt.Errorf("builders are not specified")
+ }
+ if len(flag.Args()) > 0 {
+ return fmt.Errorf("unexpected arguments: %s", flag.Args())
+ }
+
+ var startingFrom time.Time
+ var duration time.Duration
+ if *since == 0 {
+ duration = 240 * time.Hour
+ startingFrom = time.Now().Add(-duration)
+ } else {
+ startingFrom = time.Unix(*since, 0)
+ duration = time.Since(startingFrom)
+ }
+
+ for i, builder := range strings.Split(*builders, ",") {
+ builder = strings.TrimSpace(builder)
+ if builder == "" {
+ continue
+ }
+
+ if i > 0 {
+ fmt.Println()
+ }
+ fmt.Printf("builder %q\n", builder)
+ fmt.Printf("searching for all builds since timestamp %d till %d...\n",
+ startingFrom.Unix(), time.Now().Unix())
+ // We will actually fetch builds after after time.Now too, but it is fine.
+ swarmingBuilds, err := fetchBuilds(*bucket, builder+swarmingSuffix, startingFrom)
+ if err != nil {
+ return fmt.Errorf("could not fetch builds: %s", err)
+ }
+ if len(swarmingBuilds) == 0 {
+ fmt.Printf("no swarming builds for builder %q\n", builder)
+ continue
+ }
+ buildbotBuilds, err := fetchBuilds(*bucket, builder, startingFrom)
+ if err != nil {
+ return fmt.Errorf("could not fetch builds: %s", err)
+ }
+
+ swarmingBuildSets := groupBuilds(swarmingBuilds)
+ buildbotBuildSets := groupBuilds(buildbotBuilds)
+
+ consistentN := 0
+ inconsistentN := 0
+ for setName, swarmingSet := range swarmingBuildSets {
+ buildbotSet := buildbotBuildSets[setName]
+ if buildbotSet == nil {
+ fmt.Printf("no buildbot builds for buildset %s\n", setName)
+ continue
+ }
+ if buildbotSet.bestResult == swarmingSet.bestResult {
+ consistentN++
+ continue
+ }
+ inconsistentN++
+
+ fmt.Printf("%s is inconsistent\n", setName)
+ for _, b := range swarmingSet.builds {
+ fmt.Printf(" %s %s\n", b.Result, b.Url)
+ }
+ for _, b := range buildbotSet.builds {
+ fmt.Printf(" %s %s\n", b.Result, b.Url)
+ }
+ }
+
+ fmt.Printf("%0.2f%% consistent build sets, %d buildbot builds, %d swarming builds\n",
+ 100*float64(consistentN)/float64(consistentN+inconsistentN), len(buildbotBuilds), len(swarmingBuilds))
+ }
+ return nil
+}
+
+func main() {
+ if err := run(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func parseTags(tags []string) map[string]string {
+ result := make(map[string]string, len(tags))
+ for _, t := range tags {
+ parts := strings.SplitN(t, ":", 2)
+ if len(parts) == 2 {
+ result[parts[0]] = parts[1]
+ }
+ }
+ return result
+}

Powered by Google App Engine
This is Rietveld 408576698