Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Dispatcher usage: | 5 // Dispatcher usage: |
| 6 // go run infra/monitoring/dispatcher | 6 // go run infra/monitoring/dispatcher |
| 7 // Expects gatekeeper.json to be in the current directory. | 7 // Expects gatekeeper.json to be in the current directory. |
| 8 | 8 |
| 9 package main | 9 package main |
| 10 | 10 |
| 11 import ( | 11 import ( |
| 12 "encoding/json" | 12 "encoding/json" |
| 13 "expvar" | 13 "expvar" |
| 14 "flag" | 14 "flag" |
| 15 "fmt" | 15 "fmt" |
| 16 "io/ioutil" | 16 "io/ioutil" |
| 17 "net/http" | 17 "net/http" |
| 18 "os" | 18 "os" |
| 19 "sort" | |
| 19 "strings" | 20 "strings" |
| 20 "time" | 21 "time" |
| 21 | 22 |
| 22 "github.com/luci/luci-go/common/auth" | 23 "github.com/luci/luci-go/common/auth" |
| 23 "github.com/luci/luci-go/common/clock" | 24 "github.com/luci/luci-go/common/clock" |
| 24 "github.com/luci/luci-go/common/logging" | 25 "github.com/luci/luci-go/common/logging" |
| 25 "github.com/luci/luci-go/common/logging/gologger" | 26 "github.com/luci/luci-go/common/logging/gologger" |
| 26 | 27 |
| 27 "golang.org/x/net/context" | 28 "golang.org/x/net/context" |
| 28 | 29 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 48 cycleStr = flag.String("cycle", "1s", "Cycle time for loop") | 49 cycleStr = flag.String("cycle", "1s", "Cycle time for loop") |
| 49 snapshot = flag.String("record-snapshot", "", "Save a snapsho t of infra responses to this path, which will be created if it does not already exist.") | 50 snapshot = flag.String("record-snapshot", "", "Save a snapsho t of infra responses to this path, which will be created if it does not already exist.") |
| 50 replay = flag.String("replay-snapshot", "", "Replay a snaps hot of infra responses from this path, which should have been created previously by running with --record-snapshot.") | 51 replay = flag.String("replay-snapshot", "", "Replay a snaps hot of infra responses from this path, which should have been created previously by running with --record-snapshot.") |
| 51 replayTime = flag.String("replay-time", "", "Specify a simulate d starting time for the replay in RFC3339 format, used with --replay-snapshot.") | 52 replayTime = flag.String("replay-time", "", "Specify a simulate d starting time for the replay in RFC3339 format, used with --replay-snapshot.") |
| 52 serviceAccountJSON = flag.String("service-account", "", "Service accoun t JSON file.") | 53 serviceAccountJSON = flag.String("service-account", "", "Service accoun t JSON file.") |
| 53 | 54 |
| 54 log = gologger.Get() | 55 log = gologger.Get() |
| 55 duration, cycle time.Duration | 56 duration, cycle time.Duration |
| 56 | 57 |
| 57 // gk is the gatekeeper config. | 58 // gk is the gatekeeper config. |
| 58 » gk = &struct { | 59 » gk = &messages.GatekeeperConfig{} |
| 59 » » // Weird. Are there ever multiple configs per master key? | |
| 60 » » Masters map[string][]messages.MasterConfig `json:"masters"` | |
| 61 » }{} | |
| 62 // gkt is the gatekeeper trees config. | 60 // gkt is the gatekeeper trees config. |
| 63 gkt = map[string]messages.TreeMasterConfig{} | 61 gkt = map[string]messages.TreeMasterConfig{} |
| 64 filteredFailures = uint64(0) | 62 filteredFailures = uint64(0) |
| 65 expvars = expvar.NewMap("dispatcher") | 63 expvars = expvar.NewMap("dispatcher") |
| 66 ) | 64 ) |
| 67 | 65 |
| 68 func init() { | 66 func init() { |
| 69 flag.Usage = func() { | 67 flag.Usage = func() { |
| 70 fmt.Printf("By default runs a single check, saves any alerts to ./alerts.json and exits.") | 68 fmt.Printf("By default runs a single check, saves any alerts to ./alerts.json and exits.") |
| 71 flag.PrintDefaults() | 69 flag.PrintDefaults() |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 | 119 |
| 122 for range masterNames { | 120 for range masterNames { |
| 123 r := <-res | 121 r := <-res |
| 124 if r.be != nil { | 122 if r.be != nil { |
| 125 bes[r.name] = r.be | 123 bes[r.name] = r.be |
| 126 } | 124 } |
| 127 } | 125 } |
| 128 return bes | 126 return bes |
| 129 } | 127 } |
| 130 | 128 |
| 129 type bySeverity []messages.Alert | |
| 130 | |
| 131 func (a bySeverity) Len() int { return len(a) } | |
| 132 func (a bySeverity) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | |
| 133 func (a bySeverity) Less(i, j int) bool { | |
| 134 return a[i].Severity < a[j].Severity | |
| 135 } | |
| 136 | |
| 131 func mainLoop(ctx context.Context, a *analyzer.Analyzer, trees map[string]bool) error { | 137 func mainLoop(ctx context.Context, a *analyzer.Analyzer, trees map[string]bool) error { |
| 132 done := make(chan interface{}) | 138 done := make(chan interface{}) |
| 133 errs := make(chan error) | 139 errs := make(chan error) |
| 134 for treeName := range trees { | 140 for treeName := range trees { |
| 135 go func(tree string) { | 141 go func(tree string) { |
| 136 expvars.Add(fmt.Sprintf("Tree-%s", tree), 1) | 142 expvars.Add(fmt.Sprintf("Tree-%s", tree), 1) |
| 137 defer expvars.Add(fmt.Sprintf("Tree-%s", tree), -1) | 143 defer expvars.Add(fmt.Sprintf("Tree-%s", tree), -1) |
| 138 log.Infof("Checking tree: %s", tree) | 144 log.Infof("Checking tree: %s", tree) |
| 139 masterNames := []string{} | 145 masterNames := []string{} |
| 140 t := gkt[tree] | 146 t := gkt[tree] |
| 141 for _, url := range t.Masters { | 147 for _, url := range t.Masters { |
| 142 masterNames = append(masterNames, masterFromURL( url)) | 148 masterNames = append(masterNames, masterFromURL( url)) |
| 143 } | 149 } |
| 144 | 150 |
| 145 // TODO(seanmccullough): Plumb ctx through the rest of t hese calls. | 151 // TODO(seanmccullough): Plumb ctx through the rest of t hese calls. |
| 146 bes := fetchBuildExtracts(a.Reader, masterNames) | 152 bes := fetchBuildExtracts(a.Reader, masterNames) |
| 147 log.Infof("Build Extracts read: %d", len(bes)) | 153 log.Infof("Build Extracts read: %d", len(bes)) |
| 148 | 154 |
| 149 alerts := &messages.Alerts{ | 155 alerts := &messages.Alerts{ |
| 150 RevisionSummaries: map[string]messages.RevisionS ummary{}, | 156 RevisionSummaries: map[string]messages.RevisionS ummary{}, |
| 151 } | 157 } |
| 152 for masterName, be := range bes { | 158 for masterName, be := range bes { |
| 153 alerts.Alerts = append(alerts.Alerts, analyzeBui ldExtract(a, masterName, be)...) | 159 alerts.Alerts = append(alerts.Alerts, analyzeBui ldExtract(a, masterName, be)...) |
| 154 } | 160 } |
| 155 | 161 |
| 162 sort.Sort(bySeverity(alerts.Alerts)) | |
| 163 | |
| 156 // Make sure we have summaries for each revision implica ted in a builder failure. | 164 // Make sure we have summaries for each revision implica ted in a builder failure. |
| 157 for _, alert := range alerts.Alerts { | 165 for _, alert := range alerts.Alerts { |
| 158 if bf, ok := alert.Extension.(messages.BuildFail ure); ok { | 166 if bf, ok := alert.Extension.(messages.BuildFail ure); ok { |
| 159 for _, r := range bf.RegressionRanges { | 167 for _, r := range bf.RegressionRanges { |
| 160 revs, err := a.GetRevisionSummar ies(r.Revisions) | 168 revs, err := a.GetRevisionSummar ies(r.Revisions) |
| 161 if err != nil { | 169 if err != nil { |
| 162 log.Errorf("Couldn't get revision summaries: %v", err) | 170 log.Errorf("Couldn't get revision summaries: %v", err) |
| 163 continue | 171 continue |
| 164 } | 172 } |
| 165 for _, rev := range revs { | 173 for _, rev := range revs { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 260 } | 268 } |
| 261 | 269 |
| 262 err = readJSONFile(*gatekeeperJSON, &gk) | 270 err = readJSONFile(*gatekeeperJSON, &gk) |
| 263 if err != nil { | 271 if err != nil { |
| 264 log.Errorf("Error reading gatekeeper json: %v", err) | 272 log.Errorf("Error reading gatekeeper json: %v", err) |
| 265 os.Exit(1) | 273 os.Exit(1) |
| 266 } | 274 } |
| 267 | 275 |
| 268 err = readJSONFile(*gatekeeperTreesJSON, &gkt) | 276 err = readJSONFile(*gatekeeperTreesJSON, &gkt) |
| 269 if err != nil { | 277 if err != nil { |
| 270 log.Errorf("Error reading gatekeeper json: %v", err) | 278 log.Errorf("Error reading gatekeeper json: %v", err) |
|
martiniss
2016/04/11 21:33:34
Maybe update this error to say "Error reading gate
seanmccullough
2016/04/11 23:11:08
Done.
| |
| 271 os.Exit(1) | 279 os.Exit(1) |
| 272 } | 280 } |
| 273 | 281 |
| 274 if *snapshot != "" && *replay != "" { | 282 if *snapshot != "" && *replay != "" { |
| 275 log.Errorf("Cannot use snapshot and replay flags at the same tim e.") | 283 log.Errorf("Cannot use snapshot and replay flags at the same tim e.") |
| 276 os.Exit(1) | 284 os.Exit(1) |
| 277 } | 285 } |
| 278 | 286 |
| 279 r := client.NewReader(transport) | 287 r := client.NewReader(transport) |
| 280 | 288 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 291 t, err := time.Parse(time.RFC3339, *replayTime) | 299 t, err := time.Parse(time.RFC3339, *replayTime) |
| 292 if err != nil { | 300 if err != nil { |
| 293 log.Errorf("Couldn't parse replay-time: %s", err) | 301 log.Errorf("Couldn't parse replay-time: %s", err) |
| 294 os.Exit(1) | 302 os.Exit(1) |
| 295 } | 303 } |
| 296 start := time.Now() | 304 start := time.Now() |
| 297 a.Now = func() time.Time { | 305 a.Now = func() time.Time { |
| 298 diff := time.Now().Sub(start) | 306 diff := time.Now().Sub(start) |
| 299 return t.Add(diff) | 307 return t.Add(diff) |
| 300 } | 308 } |
| 309 } else if *replay != "" { | |
| 310 f, err := os.Open(*replay) | |
| 311 defer f.Close() | |
| 312 if err != nil { | |
|
martiniss
2016/04/11 21:33:34
I think you need to handle the error before you cl
seanmccullough
2016/04/11 23:11:08
defer runs its argument right before the return of
| |
| 313 log.Errorf("Couldn't open replay dir: %s", err) | |
| 314 os.Exit(1) | |
| 315 } | |
| 316 stat, err := f.Stat() | |
| 317 if err != nil { | |
| 318 log.Errorf("Couldn't stat replay dir: %s", err) | |
| 319 os.Exit(1) | |
| 320 } | |
| 321 start := time.Now() | |
| 322 t := stat.ModTime() | |
| 323 | |
| 324 a.Now = func() time.Time { | |
| 325 diff := time.Now().Sub(start) | |
| 326 return t.Add(diff) | |
| 327 } | |
| 301 } | 328 } |
| 302 | 329 |
| 303 » for masterURL, masterCfgs := range gk.Masters { | 330 » a.Gatekeeper = analyzer.NewGatekeeperRules(*gk) |
|
martiniss
2016/04/11 21:33:34
I can't seem to find this function... where is it?
seanmccullough
2016/04/11 23:11:08
Forgot to add a file. Added.
| |
| 304 » » if len(masterCfgs) != 1 { | |
| 305 » » » log.Errorf("Multiple configs for master: %s", masterURL) | |
| 306 » » } | |
| 307 » » masterName := masterFromURL(masterURL) | |
| 308 » » a.MasterCfgs[masterName] = masterCfgs[0] | |
| 309 » } | |
| 310 | 331 |
| 311 a.MasterOnly = *masterOnly | 332 a.MasterOnly = *masterOnly |
| 312 a.BuilderOnly = *builderOnly | 333 a.BuilderOnly = *builderOnly |
| 313 a.BuildOnly = *buildOnly | 334 a.BuildOnly = *buildOnly |
| 314 | 335 |
| 315 trees := map[string]bool{} | 336 trees := map[string]bool{} |
| 316 if *treesOnly != "" { | 337 if *treesOnly != "" { |
| 317 for _, treeOnly := range strings.Split(*treesOnly, ",") { | 338 for _, treeOnly := range strings.Split(*treesOnly, ",") { |
| 318 trees[treeOnly] = true | 339 trees[treeOnly] = true |
| 319 } | 340 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 340 logging.Set(ctx, log) | 361 logging.Set(ctx, log) |
| 341 defer cancel() | 362 defer cancel() |
| 342 | 363 |
| 343 loopResults := looper.Run(ctx, f, cycle, *maxErrs, clock.GetSystemClock( )) | 364 loopResults := looper.Run(ctx, f, cycle, *maxErrs, clock.GetSystemClock( )) |
| 344 | 365 |
| 345 if !loopResults.Success { | 366 if !loopResults.Success { |
| 346 log.Errorf("Failed to run loop, %v errors", loopResults.Errs) | 367 log.Errorf("Failed to run loop, %v errors", loopResults.Errs) |
| 347 os.Exit(1) | 368 os.Exit(1) |
| 348 } | 369 } |
| 349 } | 370 } |
| OLD | NEW |