| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 package swarming | |
| 16 | |
| 17 import ( | |
| 18 "errors" | |
| 19 "fmt" | |
| 20 "net/http" | |
| 21 "os" | |
| 22 | |
| 23 "golang.org/x/net/context" | |
| 24 "google.golang.org/api/googleapi" | |
| 25 | |
| 26 "github.com/luci/luci-go/common/logging" | |
| 27 "github.com/luci/luci-go/milo/common" | |
| 28 "github.com/luci/luci-go/server/router" | |
| 29 "github.com/luci/luci-go/server/templates" | |
| 30 ) | |
| 31 | |
| 32 var errUnrecognizedHost = errors.New("Unregistered Swarming Host") | |
| 33 | |
| 34 // getSwarmingHost parses the swarming hostname out of the context. If | |
| 35 // none is specified, get the default swarming host out of the global | |
| 36 // configs. | |
| 37 func getSwarmingHost(c context.Context, r *http.Request) (string, error) { | |
| 38 settings := common.GetSettings(c) | |
| 39 if settings.Swarming == nil { | |
| 40 err := errors.New("swarming not in settings") | |
| 41 logging.Errorf(c, err.Error()) | |
| 42 return "", err | |
| 43 } | |
| 44 server := r.FormValue("server") | |
| 45 // If server isn't specified, return the default host. | |
| 46 if server == "" || server == settings.Swarming.DefaultHost { | |
| 47 return settings.Swarming.DefaultHost, nil | |
| 48 } | |
| 49 // If it is specified, validate the hostname. | |
| 50 for _, hostname := range settings.Swarming.AllowedHosts { | |
| 51 if server == hostname { | |
| 52 return server, nil | |
| 53 } | |
| 54 } | |
| 55 return "", errUnrecognizedHost | |
| 56 } | |
| 57 | |
| 58 // Build is for deciphering recipe builds from swarming based off of logs. | |
| 59 type Build struct { | |
| 60 // bl is the buildLoader to use. A zero value is suitable for production
, but | |
| 61 // this can be overridden for testing. | |
| 62 bl BuildLoader | |
| 63 } | |
| 64 | |
| 65 // LogHandler writes the build log to the given response writer. | |
| 66 func LogHandler(c *router.Context) { | |
| 67 id := c.Params.ByName("id") | |
| 68 if id == "" { | |
| 69 common.ErrorPage(c, http.StatusBadRequest, "no id") | |
| 70 return | |
| 71 } | |
| 72 logname := c.Params.ByName("logname") | |
| 73 if logname == "" { | |
| 74 common.ErrorPage(c, http.StatusBadRequest, "no log name") | |
| 75 return | |
| 76 } | |
| 77 | |
| 78 hostname, err := getSwarmingHost(c.Context, c.Request) | |
| 79 if err != nil { | |
| 80 common.ErrorPage(c, http.StatusBadRequest, | |
| 81 fmt.Sprintf("no swarming host: %s", err.Error())) | |
| 82 return | |
| 83 } | |
| 84 sf, err := NewProdService(c.Context, hostname) | |
| 85 if err != nil { | |
| 86 common.ErrorPage(c, errCode(err), err.Error()) | |
| 87 return | |
| 88 } | |
| 89 | |
| 90 log, closed, err := swarmingBuildLogImpl(c.Context, sf, id, logname) | |
| 91 if err != nil { | |
| 92 common.ErrorPage(c, errCode(err), err.Error()) | |
| 93 return | |
| 94 } | |
| 95 | |
| 96 templates.MustRender(c.Context, c.Writer, "pages/log.html", templates.Ar
gs{ | |
| 97 "Log": log, | |
| 98 "Closed": closed, | |
| 99 }) | |
| 100 } | |
| 101 | |
| 102 func BuildHandler(c *router.Context) { | |
| 103 (Build{}).Render(c) | |
| 104 } | |
| 105 | |
| 106 // Render renders both the build page and the log. | |
| 107 func (b Build) Render(c *router.Context) { | |
| 108 // Get the swarming ID | |
| 109 id := c.Params.ByName("id") | |
| 110 if id == "" { | |
| 111 common.ErrorPage(c, http.StatusBadRequest, "no id") | |
| 112 return | |
| 113 } | |
| 114 | |
| 115 hostname, err := getSwarmingHost(c.Context, c.Request) | |
| 116 if err != nil { | |
| 117 common.ErrorPage(c, http.StatusBadRequest, | |
| 118 fmt.Sprintf("no swarming host: %s", err.Error())) | |
| 119 return | |
| 120 } | |
| 121 sf, err := NewProdService(c.Context, hostname) | |
| 122 if err != nil { | |
| 123 common.ErrorPage(c, errCode(err), err.Error()) | |
| 124 return | |
| 125 } | |
| 126 | |
| 127 result, err := b.bl.SwarmingBuildImpl(c.Context, sf, id) | |
| 128 if err != nil { | |
| 129 common.ErrorPage(c, errCode(err), err.Error()) | |
| 130 return | |
| 131 } | |
| 132 | |
| 133 templates.MustRender(c.Context, c.Writer, "pages/build.html", templates.
Args{ | |
| 134 "Build": result, | |
| 135 }) | |
| 136 } | |
| 137 | |
| 138 // errCode resolves recognized errors into proper http response codes. | |
| 139 func errCode(err error) int { | |
| 140 if err == errUnrecognizedHost { | |
| 141 return http.StatusBadRequest | |
| 142 } | |
| 143 if isAPINotFound(err) || os.IsNotExist(err) { | |
| 144 return http.StatusNotFound | |
| 145 } | |
| 146 return http.StatusInternalServerError | |
| 147 } | |
| 148 | |
| 149 // isAPINotFound returns true if err is a HTTP 404 API response. | |
| 150 func isAPINotFound(err error) bool { | |
| 151 if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == http.Statu
sNotFound { | |
| 152 return true | |
| 153 } | |
| 154 return false | |
| 155 } | |
| OLD | NEW |