Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(452)

Side by Side Diff: milo/appengine/swarming/html_data.go

Issue 2695383002: milo: Enable Swarming LogDog log loading. (Closed)
Patch Set: Comments, fix links. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « milo/appengine/swarming/html.go ('k') | milo/appengine/swarming/testdata/build-running-logdog » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package swarming 5 package swarming
6 6
7 import ( 7 import (
8 "encoding/json" 8 "encoding/json"
9 "fmt" 9 "fmt"
10 "io/ioutil" 10 "io/ioutil"
11 "os"
12 "path/filepath" 11 "path/filepath"
12 "sort"
13 "strings" 13 "strings"
14 "time" 14 "time"
15 15
16 "github.com/golang/protobuf/proto"
17 "github.com/luci/gae/impl/memory"
16 "golang.org/x/net/context" 18 "golang.org/x/net/context"
19 "google.golang.org/grpc"
17 20
18 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1" 21 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1"
19 "github.com/luci/luci-go/common/clock/testclock" 22 "github.com/luci/luci-go/common/clock/testclock"
20 "github.com/luci/luci-go/common/errors" 23 "github.com/luci/luci-go/common/errors"
24 miloProto "github.com/luci/luci-go/common/proto/milo"
25 "github.com/luci/luci-go/logdog/api/endpoints/coordinator/logs/v1"
26 "github.com/luci/luci-go/logdog/api/logpb"
27 "github.com/luci/luci-go/logdog/client/coordinator"
21 "github.com/luci/luci-go/milo/api/resp" 28 "github.com/luci/luci-go/milo/api/resp"
22 "github.com/luci/luci-go/milo/appengine/settings" 29 "github.com/luci/luci-go/milo/appengine/settings"
23 "github.com/luci/luci-go/server/templates" 30 "github.com/luci/luci-go/server/templates"
24 ) 31 )
25 32
26 var testCases = []struct { 33 type testCase struct {
27 » input string 34 » name string
28 » expectations string 35
29 }{ 36 » swarmResult string
30 » {"build-canceled", "build-canceled.json"}, 37 » swarmOutput string
31 » {"build-exception", "build-exception.json"}, 38 » annotations string
32 » {"build-expired", "build-expired.json"},
33 » {"build-link", "build-link.json"},
34 » {"build-patch-failure", "build-patch-failure.json"},
35 » {"build-pending", "build-pending.json"},
36 » {"build-running", "build-running.json"},
37 » {"build-timeout", "build-timeout.json"},
38 » {"build-unicode", "build-unicode.json"},
39 » {"build-nested", "build-nested.json"},
40 } 39 }
41 40
42 func getTestCases() []string { 41 func getTestCases() []*testCase {
42 » testCases := make(map[string]*testCase)
43
43 // References "milo/appengine/swarming/testdata". 44 // References "milo/appengine/swarming/testdata".
44 testdata := filepath.Join("..", "swarming", "testdata") 45 testdata := filepath.Join("..", "swarming", "testdata")
45 results := []string{}
46 f, err := ioutil.ReadDir(testdata) 46 f, err := ioutil.ReadDir(testdata)
47 if err != nil { 47 if err != nil {
48 panic(err) 48 panic(err)
49 } 49 }
50 for _, fi := range f { 50 for _, fi := range f {
51 » » if strings.HasSuffix(fi.Name(), ".swarm") { 51 » » fileName := fi.Name()
52 » » » results = append(results, fi.Name()[0:len(fi.Name())-6]) 52 » » parts := strings.SplitN(fileName, ".", 2)
53
54 » » name := parts[0]
55 » » tc := testCases[name]
56 » » if tc == nil {
57 » » » tc = &testCase{name: name}
58 » » » testCases[name] = tc
59 » » }
60
61 » » switch {
62 » » case len(parts) == 1:
63 » » » tc.swarmOutput = fileName
64 » » case parts[1] == "swarm":
65 » » » tc.swarmResult = fileName
66 » » case parts[1] == "pb.txt":
67 » » » tc.annotations = fileName
53 } 68 }
54 } 69 }
70
71 // Order test cases by name.
72 names := make([]string, 0, len(testCases))
73 for name := range testCases {
74 names = append(names, name)
75 }
76 sort.Strings(names)
77
78 results := make([]*testCase, len(names))
79 for i, name := range names {
80 results[i] = testCases[name]
81 }
82
55 return results 83 return results
56 } 84 }
57 85
58 type debugSwarmingService struct{} 86 func (tc *testCase) getContent(name string) []byte {
87 » if name == "" {
88 » » return nil
89 » }
59 90
60 func (svc debugSwarmingService) getHost() string { return "example.com" }
61
62 func (svc debugSwarmingService) getContent(taskID, suffix string) ([]byte, error ) {
63 // ../swarming below assumes that 91 // ../swarming below assumes that
64 // - this code is not executed by tests outside of this dir 92 // - this code is not executed by tests outside of this dir
65 // - this dir is a sibling of frontend dir 93 // - this dir is a sibling of frontend dir
66 » logFilename := filepath.Join("..", "swarming", "testdata", taskID) 94 » path := filepath.Join("..", "swarming", "testdata", name)
67 » if suffix != "" { 95 » data, err := ioutil.ReadFile(path)
68 » » logFilename += suffix 96 » if err != nil {
97 » » panic(fmt.Errorf("failed to read [%s]: %s", path, err))
69 } 98 }
70 » return ioutil.ReadFile(logFilename) 99 » return data
71 } 100 }
72 101
73 func (svc debugSwarmingService) getSwarmingJSON(taskID, suffix string, dst inter face{}) error { 102 func (tc *testCase) getSwarmingResult() *swarming.SwarmingRpcsTaskResult {
74 » content, err := svc.getContent(taskID, suffix) 103 » var sr swarming.SwarmingRpcsTaskResult
75 » if err != nil { 104 » data := tc.getContent(tc.swarmResult)
76 » » return err 105 » if err := json.Unmarshal(data, &sr); err != nil {
106 » » panic(fmt.Errorf("failed to unmarshal [%s]: %s", tc.swarmResult, err))
77 } 107 }
78 » return json.Unmarshal(content, dst) 108 » return &sr
79 } 109 }
80 110
111 func (tc *testCase) getSwarmingOutput() string {
112 return string(tc.getContent(tc.swarmOutput))
113 }
114
115 func (tc *testCase) getAnnotation() *miloProto.Step {
116 var step miloProto.Step
117 data := tc.getContent(tc.annotations)
118 if err := proto.UnmarshalText(string(data), &step); err != nil {
119 panic(fmt.Errorf("failed to unmarshal text protobuf [%s]: %s", t c.annotations, err))
120 }
121 return &step
122 }
123
124 type debugSwarmingService struct {
125 tc *testCase
126 }
127
128 func (svc debugSwarmingService) getHost() string { return "example.com" }
129
81 func (svc debugSwarmingService) getSwarmingResult(c context.Context, taskID stri ng) ( 130 func (svc debugSwarmingService) getSwarmingResult(c context.Context, taskID stri ng) (
82 *swarming.SwarmingRpcsTaskResult, error) { 131 *swarming.SwarmingRpcsTaskResult, error) {
83 132
84 » var sr swarming.SwarmingRpcsTaskResult 133 » return svc.tc.getSwarmingResult(), nil
85 » if err := svc.getSwarmingJSON(taskID, ".swarm", &sr); err != nil {
86 » » return nil, err
87 » }
88 » return &sr, nil
89 } 134 }
90 135
91 func (svc debugSwarmingService) getSwarmingRequest(c context.Context, taskID str ing) ( 136 func (svc debugSwarmingService) getSwarmingRequest(c context.Context, taskID str ing) (
92 *swarming.SwarmingRpcsTaskRequest, error) { 137 *swarming.SwarmingRpcsTaskRequest, error) {
93 138
94 return nil, errors.New("not implemented") 139 return nil, errors.New("not implemented")
95 } 140 }
96 141
97 func (svc debugSwarmingService) getTaskOutput(c context.Context, taskID string) (string, error) { 142 func (svc debugSwarmingService) getTaskOutput(c context.Context, taskID string) (string, error) {
98 » content, err := svc.getContent(taskID, "") 143 » return svc.tc.getSwarmingOutput(), nil
99 » if err != nil {
100 » » if os.IsNotExist(err) {
101 » » » err = nil
102 » » }
103 » » return "", err
104 » }
105 » return string(content), nil
106 } 144 }
107 145
108 // TestableLog is a subclass of Log that interfaces with TestableHandler and 146 // TestableLog is a subclass of Log that interfaces with TestableHandler and
109 // includes sample test data. 147 // includes sample test data.
110 type TestableLog struct{ Log } 148 type TestableLog struct{ Log }
111 149
112 // TestableBuild is a subclass of Build that interfaces with TestableHandler and 150 // TestableBuild is a subclass of Build that interfaces with TestableHandler and
113 // includes sample test data. 151 // includes sample test data.
114 type TestableBuild struct{ Build } 152 type TestableBuild struct{ Build }
115 153
116 // TestData returns sample test data. 154 // TestData returns sample test data.
117 func (l TestableLog) TestData() []settings.TestBundle { 155 func (l TestableLog) TestData() []settings.TestBundle {
118 return []settings.TestBundle{ 156 return []settings.TestBundle{
119 { 157 {
120 Description: "Basic log", 158 Description: "Basic log",
121 Data: templates.Args{ 159 Data: templates.Args{
122 "Log": "This is the log", 160 "Log": "This is the log",
123 "Closed": true, 161 "Closed": true,
124 }, 162 },
125 }, 163 },
126 } 164 }
127 } 165 }
128 166
167 // testLogDogClient is a minimal functional LogsClient implementation.
168 //
169 // It retains its latest input parameter and returns its configured err (if not
170 // nil) or resp.
171 type testLogDogClient struct {
172 logdog.LogsClient
173
174 req interface{}
175 resp interface{}
176 err error
177 }
178
179 func (tc *testLogDogClient) Tail(ctx context.Context, in *logdog.TailRequest, op ts ...grpc.CallOption) (
180 *logdog.GetResponse, error) {
181
182 tc.req = in
183 if tc.err != nil {
184 return nil, tc.err
185 }
186 return tc.resp.(*logdog.GetResponse), nil
187 }
188
189 func logDogClientFunc(tc *testCase) func(context.Context, string) (*coordinator. Client, error) {
190 return func(c context.Context, host string) (*coordinator.Client, error) {
191 return &coordinator.Client{
192 Host: "example.com",
193 C: &testLogDogClient{
194 resp: datagramGetResponse("testproject", "foo/ba r", tc.getAnnotation()),
195 },
196 }, nil
197 }
198 }
199
200 func datagramGetResponse(project, prefix string, msg proto.Message) *logdog.GetR esponse {
201 data, err := proto.Marshal(msg)
202 if err != nil {
203 panic(err)
204 }
205 return &logdog.GetResponse{
206 Project: project,
207 Desc: &logpb.LogStreamDescriptor{
208 Prefix: prefix,
209 ContentType: miloProto.ContentTypeAnnotations,
210 StreamType: logpb.StreamType_DATAGRAM,
211 },
212 State: &logdog.LogStreamState{},
213 Logs: []*logpb.LogEntry{
214 {
215 Content: &logpb.LogEntry_Datagram{
216 Datagram: &logpb.Datagram{
217 Data: data,
218 },
219 },
220 },
221 },
222 }
223 }
224
129 // TestData returns sample test data. 225 // TestData returns sample test data.
130 func (b TestableBuild) TestData() []settings.TestBundle { 226 func (b TestableBuild) TestData() []settings.TestBundle {
131 basic := resp.MiloBuild{ 227 basic := resp.MiloBuild{
132 Summary: resp.BuildComponent{ 228 Summary: resp.BuildComponent{
133 Label: "Test swarming build", 229 Label: "Test swarming build",
134 Status: resp.Success, 230 Status: resp.Success,
135 Started: time.Date(2016, 1, 2, 15, 4, 5, 999999999, tim e.UTC), 231 Started: time.Date(2016, 1, 2, 15, 4, 5, 999999999, tim e.UTC),
136 Finished: time.Date(2016, 1, 2, 15, 4, 6, 999999999, tim e.UTC), 232 Finished: time.Date(2016, 1, 2, 15, 4, 6, 999999999, tim e.UTC),
137 Duration: time.Second, 233 Duration: time.Second,
138 }, 234 },
139 } 235 }
140 results := []settings.TestBundle{ 236 results := []settings.TestBundle{
141 { 237 {
142 Description: "Basic successful build", 238 Description: "Basic successful build",
143 Data: templates.Args{"Build": basic}, 239 Data: templates.Args{"Build": basic},
144 }, 240 },
145 } 241 }
146 c := context.Background() 242 c := context.Background()
147 c, _ = testclock.UseTime(c, time.Date(2016, time.March, 14, 11, 0, 0, 0, time.UTC)) 243 c, _ = testclock.UseTime(c, time.Date(2016, time.March, 14, 11, 0, 0, 0, time.UTC))
244 c = memory.Use(c)
148 245
149 var svc debugSwarmingService
150 for _, tc := range getTestCases() { 246 for _, tc := range getTestCases() {
151 » » build, err := swarmingBuildImpl(c, svc, "foo", tc) 247 » » svc := debugSwarmingService{tc}
248 » » bl := buildLoader{
249 » » » logDogClientFunc: logDogClientFunc(tc),
250 » » }
251
252 » » build, err := bl.swarmingBuildImpl(c, svc, "foo", tc.name)
152 if err != nil { 253 if err != nil {
153 » » » panic(fmt.Errorf("Error while processing %s: %s", tc, er r)) 254 » » » panic(fmt.Errorf("Error while processing %s: %s", tc.nam e, err))
154 } 255 }
155 results = append(results, settings.TestBundle{ 256 results = append(results, settings.TestBundle{
156 » » » Description: tc, 257 » » » Description: tc.name,
157 Data: templates.Args{"Build": build}, 258 Data: templates.Args{"Build": build},
158 }) 259 })
159 } 260 }
160 return results 261 return results
161 } 262 }
OLDNEW
« no previous file with comments | « milo/appengine/swarming/html.go ('k') | milo/appengine/swarming/testdata/build-running-logdog » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698