| package main | |
| // lsbots lists the Swarming bots that match the given dimensions or regular expressions. | |
| // | |
| // The following example usage reboots all skia-d-gce-* bots quarantined with | |
| // "Failed to self-update the bot.": | |
| // | |
| // $ bots=`bazel run //scripts/lsbots -- --dev --include_bot "skia-d-gce-.*" --quarantined "Failed to self-update the bot"` | |
| // $ for bot in $bots; do gcloud compute ssh --zone us-central1-c --project skia-swarming-bots $bot -- sudo reboot; done | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "flag" | |
| "fmt" | |
| "regexp" | |
| "go.skia.org/infra/go/common" | |
| "go.skia.org/infra/go/httputils" | |
| "go.skia.org/infra/go/sklog" | |
| "go.skia.org/infra/go/swarming" | |
| "golang.org/x/oauth2/google" | |
| ) | |
| func main() { | |
| dev := flag.Bool("dev", false, "Run against dev swarming instance.") | |
| internal := flag.Bool("internal", false, "Run against internal swarming instance.") | |
| pool := flag.String("pool", swarming.DIMENSION_POOL_VALUE_SKIA, "Which Swarming pool to use.") | |
| dimensions := common.NewMultiStringFlag("dimension", nil, "Colon-separated key/value pair, eg: \"os:Linux\" dimensions of the bots to list. Can specify multiple times.") | |
| includeBots := common.NewMultiStringFlag("include_bot", nil, "Include these bots, regardless of whether they match the requested dimensions. Calculated AFTER the dimensions are computed. Can be simple strings or regexes.") | |
| excludeBots := common.NewMultiStringFlag("exclude_bot", nil, "Exclude these bots, regardless of whether they match the requested dimensions. Calculated AFTER the dimensions are computed and after --include_bot is applied. Can be simple strings or regexes.") | |
| quarantined := flag.String("quarantined", "", "Only include quarantined bots whose quarantine reason matches this regular expression.") | |
| // Setup, parse args. | |
| common.Init() | |
| ctx := context.Background() | |
| if *internal && *dev { | |
| sklog.Fatal("Both --internal and --dev cannot be specified.") | |
| } | |
| dims, err := swarming.ParseDimensionsSingleValue(*dimensions) | |
| if err != nil { | |
| sklog.Fatalf("Problem parsing dimensions: %s", err) | |
| } | |
| dims["pool"] = *pool | |
| includeRegs, err := parseRegex(*includeBots) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| excludeRegs, err := parseRegex(*excludeBots) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| quarantinedRegexp, err := regexp.Compile(*quarantined) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| swarmingServer := swarming.SWARMING_SERVER | |
| if *internal { | |
| swarmingServer = swarming.SWARMING_SERVER_PRIVATE | |
| } else if *dev { | |
| swarmingServer = swarming.SWARMING_SERVER_DEV | |
| } | |
| // Authenticated HTTP client. | |
| ts, err := google.DefaultTokenSource(ctx, swarming.AUTH_SCOPE) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client() | |
| // Swarming API client. | |
| swarmAPI, err := swarming.NewApiClient(httpClient, swarmingServer) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| // Obtain the list of bots. | |
| bots, err := swarmAPI.ListBots(ctx, dims) | |
| if err != nil { | |
| sklog.Fatal(err) | |
| } | |
| for _, bot := range bots { | |
| if len(includeRegs) > 0 && !matchesAny(bot.BotId, includeRegs) { | |
| sklog.Debugf("Skipping %s because it does not match --include_bot", bot.BotId) | |
| continue | |
| } | |
| if matchesAny(bot.BotId, excludeRegs) { | |
| sklog.Debugf("Skipping %s because it matches --exclude_bot", bot.BotId) | |
| continue | |
| } | |
| if *quarantined != "" { | |
| if !bot.Quarantined { | |
| sklog.Debugf("Skipping %s because it is not quarantined, and --quarantined was provided", bot.BotId) | |
| continue | |
| } | |
| state := &struct { | |
| Quarantined string `json:"quarantined"` | |
| }{} | |
| if err := json.Unmarshal([]byte(bot.State), &state); err != nil { | |
| sklog.Fatal(err) | |
| } | |
| if !quarantinedRegexp.MatchString(state.Quarantined) { | |
| sklog.Debugf("Skipping %s because its quarantine reason does not match --quarantined", bot.BotId) | |
| continue | |
| } | |
| } | |
| fmt.Println(bot.BotId) | |
| } | |
| } | |
| func parseRegex(flags []string) ([]*regexp.Regexp, error) { | |
| var regexps []*regexp.Regexp | |
| for _, s := range flags { | |
| r, err := regexp.Compile(s) | |
| if err != nil { | |
| return nil, err | |
| } | |
| regexps = append(regexps, r) | |
| } | |
| return regexps, nil | |
| } | |
| func matchesAny(s string, xr []*regexp.Regexp) bool { | |
| for _, r := range xr { | |
| if r.MatchString(s) { | |
| return true | |
| } | |
| } | |
| return false | |
| } |