| 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 |
| } |