| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 // Package som implements HTTP server that handles requests to default module. | 5 // Package som implements HTTP server that handles requests to default module. |
| 6 package som | 6 package som |
| 7 | 7 |
| 8 import ( | 8 import ( |
| 9 "crypto/sha1" | 9 "crypto/sha1" |
| 10 "encoding/json" | 10 "encoding/json" |
| 11 "fmt" | 11 "fmt" |
| 12 "html/template" | 12 "html/template" |
| 13 "infra/monorail" | 13 "infra/monorail" |
| 14 "io/ioutil" | 14 "io/ioutil" |
| 15 "net/http" | 15 "net/http" |
| 16 "strings" | 16 "strings" |
| 17 | 17 |
| 18 "github.com/julienschmidt/httprouter" | |
| 19 "golang.org/x/net/context" | 18 "golang.org/x/net/context" |
| 20 "google.golang.org/appengine" | 19 "google.golang.org/appengine" |
| 21 | 20 |
| 22 "github.com/luci/gae/service/datastore" | 21 "github.com/luci/gae/service/datastore" |
| 23 "github.com/luci/gae/service/urlfetch" | 22 "github.com/luci/gae/service/urlfetch" |
| 24 "github.com/luci/luci-go/appengine/gaeauth/client" | 23 "github.com/luci/luci-go/appengine/gaeauth/client" |
| 25 "github.com/luci/luci-go/appengine/gaeauth/server" | 24 "github.com/luci/luci-go/appengine/gaeauth/server" |
| 26 "github.com/luci/luci-go/appengine/gaemiddleware" | 25 "github.com/luci/luci-go/appengine/gaemiddleware" |
| 27 "github.com/luci/luci-go/common/clock" | 26 "github.com/luci/luci-go/common/clock" |
| 28 "github.com/luci/luci-go/common/logging" | 27 "github.com/luci/luci-go/common/logging" |
| 29 "github.com/luci/luci-go/server/auth" | 28 "github.com/luci/luci-go/server/auth" |
| 30 "github.com/luci/luci-go/server/auth/identity" | 29 "github.com/luci/luci-go/server/auth/identity" |
| 31 » "github.com/luci/luci-go/server/middleware" | 30 » "github.com/luci/luci-go/server/router" |
| 32 "github.com/luci/luci-go/server/settings" | 31 "github.com/luci/luci-go/server/settings" |
| 33 ) | 32 ) |
| 34 | 33 |
| 35 const authGroup = "sheriff-o-matic-access" | 34 const authGroup = "sheriff-o-matic-access" |
| 36 | 35 |
| 37 var ( | 36 var ( |
| 38 mainPage = template.Must(template.ParseFiles("./index.html")) | 37 mainPage = template.Must(template.ParseFiles("./index.html")) |
| 39 accessDeniedPage = template.Must(template.ParseFiles("./access-denied.ht
ml")) | 38 accessDeniedPage = template.Must(template.ParseFiles("./access-denied.ht
ml")) |
| 40 monorailEndpoint = "https://monorail-prod.appspot.com/_ah/api/monorail/v
1/" | 39 monorailEndpoint = "https://monorail-prod.appspot.com/_ah/api/monorail/v
1/" |
| 41 ) | 40 ) |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 if alertStreams, ok := values["AlertStreams"]; ok && alertStreams != ""
{ | 168 if alertStreams, ok := values["AlertStreams"]; ok && alertStreams != ""
{ |
| 170 if err := writeAlertStreams(c, alertStreams); err != nil { | 169 if err := writeAlertStreams(c, alertStreams); err != nil { |
| 171 return err | 170 return err |
| 172 } | 171 } |
| 173 } | 172 } |
| 174 | 173 |
| 175 return nil | 174 return nil |
| 176 } | 175 } |
| 177 | 176 |
| 178 //// Handlers. | 177 //// Handlers. |
| 179 func indexPage(c context.Context, w http.ResponseWriter, r *http.Request, p http
router.Params) { | 178 func indexPage(ctx *router.Context) { |
| 179 » c, w, r, p := ctx.Context, ctx.Writer, ctx.Request, ctx.Params |
| 180 if p.ByName("path") == "" { | 180 if p.ByName("path") == "" { |
| 181 http.Redirect(w, r, "/chromium", http.StatusFound) | 181 http.Redirect(w, r, "/chromium", http.StatusFound) |
| 182 return | 182 return |
| 183 } | 183 } |
| 184 | 184 |
| 185 user := auth.CurrentIdentity(c) | 185 user := auth.CurrentIdentity(c) |
| 186 | 186 |
| 187 if user.Kind() == identity.Anonymous { | 187 if user.Kind() == identity.Anonymous { |
| 188 url, err := auth.LoginURL(c, "/") | 188 url, err := auth.LoginURL(c, "/") |
| 189 if err != nil { | 189 if err != nil { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 "User": user.Email(), | 225 "User": user.Email(), |
| 226 "LogoutUrl": logoutURL, | 226 "LogoutUrl": logoutURL, |
| 227 } | 227 } |
| 228 | 228 |
| 229 err = mainPage.Execute(w, data) | 229 err = mainPage.Execute(w, data) |
| 230 if err != nil { | 230 if err != nil { |
| 231 logging.Errorf(c, "while rendering index: %s", err) | 231 logging.Errorf(c, "while rendering index: %s", err) |
| 232 } | 232 } |
| 233 } | 233 } |
| 234 | 234 |
| 235 func getTreesHandler(c context.Context, w http.ResponseWriter, r *http.Request,
p httprouter.Params) { | 235 func getTreesHandler(ctx *router.Context) { |
| 236 » c, w := ctx.Context, ctx.Writer |
| 237 |
| 236 if !requireGoogler(w, c) { | 238 if !requireGoogler(w, c) { |
| 237 return | 239 return |
| 238 } | 240 } |
| 239 | 241 |
| 240 q := datastore.NewQuery("Tree") | 242 q := datastore.NewQuery("Tree") |
| 241 results := []*Tree{} | 243 results := []*Tree{} |
| 242 err := datastore.Get(c).GetAll(q, &results) | 244 err := datastore.Get(c).GetAll(q, &results) |
| 243 if err != nil { | 245 if err != nil { |
| 244 errStatus(w, http.StatusInternalServerError, err.Error()) | 246 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 245 return | 247 return |
| 246 } | 248 } |
| 247 | 249 |
| 248 txt, err := json.Marshal(results) | 250 txt, err := json.Marshal(results) |
| 249 if err != nil { | 251 if err != nil { |
| 250 errStatus(w, http.StatusInternalServerError, err.Error()) | 252 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 251 return | 253 return |
| 252 } | 254 } |
| 253 | 255 |
| 254 w.Header().Set("Content-Type", "application/json") | 256 w.Header().Set("Content-Type", "application/json") |
| 255 w.Write(txt) | 257 w.Write(txt) |
| 256 } | 258 } |
| 257 | 259 |
| 258 func getAlertsHandler(c context.Context, w http.ResponseWriter, r *http.Request,
p httprouter.Params) { | 260 func getAlertsHandler(ctx *router.Context) { |
| 261 » c, w, p := ctx.Context, ctx.Writer, ctx.Params |
| 262 |
| 259 if !requireGoogler(w, c) { | 263 if !requireGoogler(w, c) { |
| 260 return | 264 return |
| 261 } | 265 } |
| 262 | 266 |
| 263 ds := datastore.Get(c) | 267 ds := datastore.Get(c) |
| 264 | 268 |
| 265 tree := p.ByName("tree") | 269 tree := p.ByName("tree") |
| 266 q := datastore.NewQuery("AlertsJSON") | 270 q := datastore.NewQuery("AlertsJSON") |
| 267 q = q.Ancestor(ds.MakeKey("Tree", tree)) | 271 q = q.Ancestor(ds.MakeKey("Tree", tree)) |
| 268 q = q.Order("-Date") | 272 q = q.Order("-Date") |
| (...skipping 10 matching lines...) Expand all Loading... |
| 279 logging.Warningf(c, "No alerts found for tree %s", tree) | 283 logging.Warningf(c, "No alerts found for tree %s", tree) |
| 280 errStatus(w, http.StatusNotFound, fmt.Sprintf("Tree \"%s\" not f
ound", tree)) | 284 errStatus(w, http.StatusNotFound, fmt.Sprintf("Tree \"%s\" not f
ound", tree)) |
| 281 return | 285 return |
| 282 } | 286 } |
| 283 | 287 |
| 284 alertsJSON := results[0] | 288 alertsJSON := results[0] |
| 285 w.Header().Set("Content-Type", "application/json") | 289 w.Header().Set("Content-Type", "application/json") |
| 286 w.Write(alertsJSON.Contents) | 290 w.Write(alertsJSON.Contents) |
| 287 } | 291 } |
| 288 | 292 |
| 289 func postAlertsHandler(c context.Context, w http.ResponseWriter, r *http.Request
, p httprouter.Params) { | 293 func postAlertsHandler(ctx *router.Context) { |
| 294 » c, w, r, p := ctx.Context, ctx.Writer, ctx.Request, ctx.Params |
| 295 |
| 290 if !requireGoogler(w, c) { | 296 if !requireGoogler(w, c) { |
| 291 return | 297 return |
| 292 } | 298 } |
| 293 | 299 |
| 294 tree := p.ByName("tree") | 300 tree := p.ByName("tree") |
| 295 ds := datastore.Get(c) | 301 ds := datastore.Get(c) |
| 296 | 302 |
| 297 alerts := AlertsJSON{ | 303 alerts := AlertsJSON{ |
| 298 Tree: ds.MakeKey("Tree", tree), | 304 Tree: ds.MakeKey("Tree", tree), |
| 299 Date: clock.Now(c), | 305 Date: clock.Now(c), |
| (...skipping 26 matching lines...) Expand all Loading... |
| 326 } | 332 } |
| 327 | 333 |
| 328 alerts.Contents = data | 334 alerts.Contents = data |
| 329 err = datastore.Get(c).Put(&alerts) | 335 err = datastore.Get(c).Put(&alerts) |
| 330 if err != nil { | 336 if err != nil { |
| 331 errStatus(w, http.StatusInternalServerError, err.Error()) | 337 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 332 return | 338 return |
| 333 } | 339 } |
| 334 } | 340 } |
| 335 | 341 |
| 336 func getAnnotationsHandler(c context.Context, w http.ResponseWriter, r *http.Req
uest, p httprouter.Params) { | 342 func getAnnotationsHandler(ctx *router.Context) { |
| 343 » c, w := ctx.Context, ctx.Writer |
| 344 |
| 337 if !requireGoogler(w, c) { | 345 if !requireGoogler(w, c) { |
| 338 return | 346 return |
| 339 } | 347 } |
| 340 | 348 |
| 341 q := datastore.NewQuery("Annotation") | 349 q := datastore.NewQuery("Annotation") |
| 342 results := []*Annotation{} | 350 results := []*Annotation{} |
| 343 datastore.Get(c).GetAll(q, &results) | 351 datastore.Get(c).GetAll(q, &results) |
| 344 | 352 |
| 345 data, err := json.Marshal(results) | 353 data, err := json.Marshal(results) |
| 346 if err != nil { | 354 if err != nil { |
| 347 errStatus(w, http.StatusInternalServerError, err.Error()) | 355 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 348 return | 356 return |
| 349 } | 357 } |
| 350 | 358 |
| 351 w.Header().Set("Content-Type", "application/json") | 359 w.Header().Set("Content-Type", "application/json") |
| 352 w.Write(data) | 360 w.Write(data) |
| 353 } | 361 } |
| 354 | 362 |
| 355 func postAnnotationsHandler(c context.Context, w http.ResponseWriter, r *http.Re
quest, p httprouter.Params) { | 363 func postAnnotationsHandler(ctx *router.Context) { |
| 364 » c, w, r, p := ctx.Context, ctx.Writer, ctx.Request, ctx.Params |
| 365 |
| 356 if !requireGoogler(w, c) { | 366 if !requireGoogler(w, c) { |
| 357 return | 367 return |
| 358 } | 368 } |
| 359 | 369 |
| 360 annKey := p.ByName("annKey") | 370 annKey := p.ByName("annKey") |
| 361 action := p.ByName("action") | 371 action := p.ByName("action") |
| 362 ds := datastore.Get(c) | 372 ds := datastore.Get(c) |
| 363 | 373 |
| 364 if !(action == "add" || action == "remove") { | 374 if !(action == "add" || action == "remove") { |
| 365 errStatus(w, http.StatusNotFound, "Invalid action") | 375 errStatus(w, http.StatusNotFound, "Invalid action") |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 404 data, err := json.Marshal(annotation) | 414 data, err := json.Marshal(annotation) |
| 405 if err != nil { | 415 if err != nil { |
| 406 errStatus(w, http.StatusInternalServerError, err.Error()) | 416 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 407 return | 417 return |
| 408 } | 418 } |
| 409 | 419 |
| 410 w.Header().Set("Content-Type", "application/json") | 420 w.Header().Set("Content-Type", "application/json") |
| 411 w.Write(data) | 421 w.Write(data) |
| 412 } | 422 } |
| 413 | 423 |
| 414 func getBugQueueHandler(c context.Context, w http.ResponseWriter, r *http.Reques
t, p httprouter.Params) { | 424 func getBugQueueHandler(ctx *router.Context) { |
| 425 » c, w, p := ctx.Context, ctx.Writer, ctx.Params |
| 426 |
| 415 c = client.UseServiceAccountTransport(c, nil, nil) | 427 c = client.UseServiceAccountTransport(c, nil, nil) |
| 416 mr := monorail.NewEndpointsClient(&http.Client{Transport: urlfetch.Get(c
)}, monorailEndpoint) | 428 mr := monorail.NewEndpointsClient(&http.Client{Transport: urlfetch.Get(c
)}, monorailEndpoint) |
| 417 tree := p.ByName("tree") | 429 tree := p.ByName("tree") |
| 418 | 430 |
| 419 // TODO(martiniss): make this look up request info based on Tree datasto
re | 431 // TODO(martiniss): make this look up request info based on Tree datasto
re |
| 420 // object | 432 // object |
| 421 req := &monorail.IssuesListRequest{ | 433 req := &monorail.IssuesListRequest{ |
| 422 ProjectId: tree, | 434 ProjectId: tree, |
| 423 Can: monorail.IssuesListRequest_OPEN, | 435 Can: monorail.IssuesListRequest_OPEN, |
| 424 Q: fmt.Sprintf("label:Sheriff-%s", tree), | 436 Q: fmt.Sprintf("label:Sheriff-%s", tree), |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 | 470 |
| 459 m := map[string]string{} | 471 m := map[string]string{} |
| 460 err = json.Unmarshal(body, &m) | 472 err = json.Unmarshal(body, &m) |
| 461 if err != nil { | 473 if err != nil { |
| 462 return nil, err | 474 return nil, err |
| 463 } | 475 } |
| 464 | 476 |
| 465 return m, nil | 477 return m, nil |
| 466 } | 478 } |
| 467 | 479 |
| 468 func getRevRangeHandler(c context.Context, w http.ResponseWriter, r *http.Reques
t, p httprouter.Params) { | 480 func getRevRangeHandler(ctx *router.Context) { |
| 481 » c, w, r, p := ctx.Context, ctx.Writer, ctx.Request, ctx.Params |
| 482 |
| 469 start := p.ByName("start") | 483 start := p.ByName("start") |
| 470 end := p.ByName("end") | 484 end := p.ByName("end") |
| 471 if start == "" || end == "" { | 485 if start == "" || end == "" { |
| 472 errStatus(w, http.StatusBadRequest, "Start and end parameters mu
st be set.") | 486 errStatus(w, http.StatusBadRequest, "Start and end parameters mu
st be set.") |
| 473 return | 487 return |
| 474 } | 488 } |
| 475 | 489 |
| 476 startRev, err := getCrRevJSON(c, start) | 490 startRev, err := getCrRevJSON(c, start) |
| 477 if err != nil { | 491 if err != nil { |
| 478 errStatus(w, http.StatusInternalServerError, err.Error()) | 492 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 479 return | 493 return |
| 480 } | 494 } |
| 481 | 495 |
| 482 endRev, err := getCrRevJSON(c, end) | 496 endRev, err := getCrRevJSON(c, end) |
| 483 if err != nil { | 497 if err != nil { |
| 484 errStatus(w, http.StatusInternalServerError, err.Error()) | 498 errStatus(w, http.StatusInternalServerError, err.Error()) |
| 485 return | 499 return |
| 486 } | 500 } |
| 487 | 501 |
| 488 // TODO(seanmccullough): some sanity checking of the rev json (same repo
etc) | 502 // TODO(seanmccullough): some sanity checking of the rev json (same repo
etc) |
| 489 | 503 |
| 490 gitilesURL := fmt.Sprintf("https://chromium.googlesource.com/chromium/sr
c/+log/%s^..%s?format=JSON", | 504 gitilesURL := fmt.Sprintf("https://chromium.googlesource.com/chromium/sr
c/+log/%s^..%s?format=JSON", |
| 491 startRev["git_sha"], endRev["git_sha"]) | 505 startRev["git_sha"], endRev["git_sha"]) |
| 492 | 506 |
| 493 http.Redirect(w, r, gitilesURL, 301) | 507 http.Redirect(w, r, gitilesURL, 301) |
| 494 } | 508 } |
| 495 | 509 |
| 496 // base is the root of the middleware chain. | 510 // base is the root of the middleware chain. |
| 497 func base(h middleware.Handler) httprouter.Handle { | 511 func base() router.MiddlewareChain { |
| 498 methods := auth.Authenticator{ | 512 methods := auth.Authenticator{ |
| 499 &server.OAuth2Method{Scopes: []string{server.EmailScope}}, | 513 &server.OAuth2Method{Scopes: []string{server.EmailScope}}, |
| 500 server.CookieAuth, | 514 server.CookieAuth, |
| 501 &server.InboundAppIDAuthMethod{}, | 515 &server.InboundAppIDAuthMethod{}, |
| 502 } | 516 } |
| 503 » h = auth.Use(h, methods) | 517 » return append( |
| 504 » if !appengine.IsDevAppServer() { | 518 » » gaemiddleware.BaseProd(), |
| 505 » » h = middleware.WithPanicCatcher(h) | 519 » » auth.Use(methods), |
| 506 » } | 520 » ) |
| 507 » return gaemiddleware.BaseProd(h) | |
| 508 } | 521 } |
| 509 | 522 |
| 510 //// Routes. | 523 //// Routes. |
| 511 func init() { | 524 func init() { |
| 512 settings.RegisterUIPage(settingsKey, settingsUIPage{}) | 525 settings.RegisterUIPage(settingsKey, settingsUIPage{}) |
| 513 | 526 |
| 514 » router := httprouter.New() | 527 » r := router.New() |
| 515 » gaemiddleware.InstallHandlers(router, base) | 528 » basemw := base() |
| 516 » router.GET("/api/v1/trees/", base(auth.Authenticate(getTreesHandler))) | 529 » authmw := append(basemw, auth.Authenticate) |
| 517 » router.GET("/api/v1/alerts/:tree", base(auth.Authenticate(getAlertsHandl
er))) | |
| 518 » router.POST("/api/v1/alerts/:tree", base(auth.Authenticate(postAlertsHan
dler))) | |
| 519 » router.GET("/api/v1/annotations/", base(auth.Authenticate(getAnnotations
Handler))) | |
| 520 » router.POST("/api/v1/annotations/:annKey/:action", base(auth.Authenticat
e(postAnnotationsHandler))) | |
| 521 » router.GET("/api/v1/bugqueue/:tree", base(auth.Authenticate(getBugQueueH
andler))) | |
| 522 » router.GET("/api/v1/revrange/:start/:end", base(getRevRangeHandler)) | |
| 523 | 530 |
| 524 » rootRouter := httprouter.New() | 531 » gaemiddleware.InstallHandlers(r, basemw) |
| 525 » rootRouter.GET("/*path", base(auth.Authenticate(indexPage))) | 532 » r.GET("/api/v1/trees/", authmw, getTreesHandler) |
| 533 » r.GET("/api/v1/alerts/:tree", authmw, getAlertsHandler) |
| 534 » r.POST("/api/v1/alerts/:tree", authmw, postAlertsHandler) |
| 535 » r.GET("/api/v1/annotations/", authmw, getAnnotationsHandler) |
| 536 » r.POST("/api/v1/annotations/:annKey/:action", authmw, postAnnotationsHan
dler) |
| 537 » r.GET("/api/v1/bugqueue/:tree", authmw, getBugQueueHandler) |
| 538 » r.GET("/api/v1/revrange/:start/:end", basemw, getRevRangeHandler) |
| 526 | 539 |
| 527 » http.DefaultServeMux.Handle("/api/", router) | 540 » rootRouter := router.New() |
| 528 » http.DefaultServeMux.Handle("/admin/", router) | 541 » rootRouter.GET("/*path", authmw, indexPage) |
| 529 » http.DefaultServeMux.Handle("/auth/", router) | 542 |
| 530 » http.DefaultServeMux.Handle("/_ah/", router) | 543 » http.DefaultServeMux.Handle("/api/", r) |
| 544 » http.DefaultServeMux.Handle("/admin/", r) |
| 545 » http.DefaultServeMux.Handle("/auth/", r) |
| 546 » http.DefaultServeMux.Handle("/_ah/", r) |
| 531 http.DefaultServeMux.Handle("/", rootRouter) | 547 http.DefaultServeMux.Handle("/", rootRouter) |
| 532 } | 548 } |
| OLD | NEW |