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 |