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

Side by Side Diff: milo/appengine/logdog/build.go

Issue 2674603002: milo: Export LogDog annotation stream, remove host (Closed)
Patch Set: 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/frontend/milo.go ('k') | milo/appengine/logdog/http.go » ('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 2016 The LUCI Authors. All rights reserved. 1 // Copyright 2016 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 logdog 5 package logdog
6 6
7 import ( 7 import (
8 "errors"
9 "fmt" 8 "fmt"
10 "net/http" 9 "net/http"
11 "net/url"
12 "strings"
13 "time" 10 "time"
14 11
15 log "github.com/luci/luci-go/common/logging" 12 log "github.com/luci/luci-go/common/logging"
16 "github.com/luci/luci-go/common/proto/google" 13 "github.com/luci/luci-go/common/proto/google"
17 miloProto "github.com/luci/luci-go/common/proto/milo" 14 miloProto "github.com/luci/luci-go/common/proto/milo"
18 "github.com/luci/luci-go/grpc/grpcutil" 15 "github.com/luci/luci-go/grpc/grpcutil"
19 "github.com/luci/luci-go/logdog/api/logpb" 16 "github.com/luci/luci-go/logdog/api/logpb"
20 "github.com/luci/luci-go/logdog/client/coordinator" 17 "github.com/luci/luci-go/logdog/client/coordinator"
21 "github.com/luci/luci-go/logdog/common/types" 18 "github.com/luci/luci-go/logdog/common/types"
19 "github.com/luci/luci-go/logdog/common/viewer"
22 "github.com/luci/luci-go/luci_config/common/cfgtypes" 20 "github.com/luci/luci-go/luci_config/common/cfgtypes"
23 "github.com/luci/luci-go/milo/api/resp" 21 "github.com/luci/luci-go/milo/api/resp"
24 "github.com/luci/luci-go/milo/appengine/logdog/internal" 22 "github.com/luci/luci-go/milo/appengine/logdog/internal"
25 "github.com/luci/luci-go/milo/common/miloerror" 23 "github.com/luci/luci-go/milo/common/miloerror"
26 24
27 "github.com/golang/protobuf/proto" 25 "github.com/golang/protobuf/proto"
28 mc "github.com/luci/gae/service/memcache" 26 mc "github.com/luci/gae/service/memcache"
29 "golang.org/x/net/context" 27 "golang.org/x/net/context"
30 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/codes"
31 ) 29 )
32 30
33 const ( 31 const (
34 // intermediateCacheLifetime is the amount of time to cache intermediate (non- 32 // intermediateCacheLifetime is the amount of time to cache intermediate (non-
35 // terminal) annotation streams. Terminal annotation streams are cached 33 // terminal) annotation streams. Terminal annotation streams are cached
36 // indefinitely. 34 // indefinitely.
37 intermediateCacheLifetime = 10 * time.Second 35 intermediateCacheLifetime = 10 * time.Second
38 36
39 // defaultLogDogHost is the default LogDog host, if one isn't specified via 37 // defaultLogDogHost is the default LogDog host, if one isn't specified via
40 // query string. 38 // query string.
41 defaultLogDogHost = "luci-logdog.appspot.com" 39 defaultLogDogHost = "luci-logdog.appspot.com"
42 ) 40 )
43 41
44 type annotationStreamRequest struct { 42 // AnnotationStream represents a LogDog annotation protobuf stream.
45 » *AnnotationStream 43 type AnnotationStream struct {
46 44 » Project cfgtypes.ProjectName
47 » // host is the name of the LogDog host. 45 » Path types.StreamPath
48 » host string
49
50 » project cfgtypes.ProjectName
51 » path types.StreamPath
52 46
53 // logDogClient is the HTTP client to use for LogDog communication. 47 // logDogClient is the HTTP client to use for LogDog communication.
54 » logDogClient *coordinator.Client 48 » Client *coordinator.Client
55 49
56 // cs is the unmarshalled annotation stream Step and associated data. 50 // cs is the unmarshalled annotation stream Step and associated data.
57 cs internal.CachedStep 51 cs internal.CachedStep
58 } 52 }
59 53
60 func (as *annotationStreamRequest) normalize() error { 54 // Normalize validates and normalizes the stream's parameters.
61 » if err := as.project.Validate(); err != nil { 55 func (as *AnnotationStream) Normalize() error {
62 » » return &miloerror.Error{ 56 » if err := as.Project.Validate(); err != nil {
63 » » » Message: "Invalid project name", 57 » » return fmt.Errorf("Invalid project name: %s", as.Project)
hinoka 2017/02/02 22:09:25 This makes it a 500 instead of a 400, is that inte
dnj 2017/02/02 22:29:00 oops - I moved that logic into "html.go" now.
64 » » » Code: http.StatusBadRequest,
65 » » }
66 } 58 }
67 59
68 » if err := as.path.Validate(); err != nil { 60 » if err := as.Path.Validate(); err != nil {
69 » » return &miloerror.Error{ 61 » » return fmt.Errorf("Invalid log stream path %q: %s", as.Path, err )
hinoka 2017/02/02 22:09:25 Same here
dnj 2017/02/02 22:29:00 Done.
70 » » » Message: fmt.Sprintf("Invalid log stream path %q: %s", a s.path, err),
71 » » » Code: http.StatusBadRequest,
72 » » }
73 » }
74
75 » // Get the host. We normalize it to lowercase and trim spaces since we u se
76 » // it as a memcache key.
77 » as.host = strings.ToLower(strings.TrimSpace(as.host))
78 » if as.host == "" {
79 » » as.host = defaultLogDogHost
80 » }
81 » if strings.ContainsRune(as.host, '/') {
82 » » return errors.New("invalid host name")
83 } 62 }
84 63
85 return nil 64 return nil
86 } 65 }
87 66
88 func (as *annotationStreamRequest) memcacheKey() string { 67 // Load loads (or re-loads) the annotation stream from LogDog.
89 » return fmt.Sprintf("logdog/%s/%s/%s", as.host, as.project, as.path) 68 func (as *AnnotationStream) Load(c context.Context) error {
90 }
91
92 func (as *annotationStreamRequest) load(c context.Context) error {
93 // Load from memcache, if possible. If an error occurs, we will proceed as if 69 // Load from memcache, if possible. If an error occurs, we will proceed as if
94 // no CachedStep was available. 70 // no CachedStep was available.
95 mcKey := as.memcacheKey() 71 mcKey := as.memcacheKey()
96 mcItem, err := mc.GetKey(c, mcKey) 72 mcItem, err := mc.GetKey(c, mcKey)
97 switch err { 73 switch err {
98 case nil: 74 case nil:
99 if err := proto.Unmarshal(mcItem.Value(), &as.cs); err == nil { 75 if err := proto.Unmarshal(mcItem.Value(), &as.cs); err == nil {
100 return nil 76 return nil
101 } 77 }
102 78
(...skipping 12 matching lines...) Expand all
115 91
116 default: 92 default:
117 log.Fields{ 93 log.Fields{
118 log.ErrorKey: err, 94 log.ErrorKey: err,
119 "memcacheKey": mcKey, 95 "memcacheKey": mcKey,
120 }.Errorf(c, "Failed to load annotation protobuf memcache cached step.") 96 }.Errorf(c, "Failed to load annotation protobuf memcache cached step.")
121 } 97 }
122 98
123 // Load from LogDog directly. 99 // Load from LogDog directly.
124 log.Fields{ 100 log.Fields{
125 » » "project": as.project, 101 » » "host": as.Client.Host,
126 » » "path": as.path, 102 » » "project": as.Project,
127 » » "host": as.host, 103 » » "path": as.Path,
128 }.Infof(c, "Making tail request to LogDog to fetch annotation stream.") 104 }.Infof(c, "Making tail request to LogDog to fetch annotation stream.")
129 105
130 var ( 106 var (
131 state coordinator.LogStream 107 state coordinator.LogStream
132 » » stream = as.logDogClient.Stream(as.project, as.path) 108 » » stream = as.Client.Stream(as.Project, as.Path)
133 ) 109 )
134 le, err := stream.Tail(c, coordinator.WithState(&state), coordinator.Com plete()) 110 le, err := stream.Tail(c, coordinator.WithState(&state), coordinator.Com plete())
135 switch code := grpcutil.Code(err); code { 111 switch code := grpcutil.Code(err); code {
136 case codes.OK: 112 case codes.OK:
137 break 113 break
138 114
139 case codes.NotFound: 115 case codes.NotFound:
140 return &miloerror.Error{ 116 return &miloerror.Error{
141 Message: "Stream not found", 117 Message: "Stream not found",
142 Code: http.StatusNotFound, 118 Code: http.StatusNotFound,
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 if err := mc.Set(c, mcItem); err != nil { 218 if err := mc.Set(c, mcItem); err != nil {
243 log.WithError(err).Warningf(c, "Failed to cache annotati on protobuf CachedStep.") 219 log.WithError(err).Warningf(c, "Failed to cache annotati on protobuf CachedStep.")
244 } 220 }
245 } else { 221 } else {
246 log.WithError(err).Warningf(c, "Failed to marshal annotation pro tobuf CachedStep.") 222 log.WithError(err).Warningf(c, "Failed to marshal annotation pro tobuf CachedStep.")
247 } 223 }
248 224
249 return nil 225 return nil
250 } 226 }
251 227
252 func (as *annotationStreamRequest) toMiloBuild(c context.Context) *resp.MiloBuil d { 228 // Step returns the loaded Step.
253 » prefix, name := as.path.Split() 229 //
230 // This must be populated via Load, or else it will return nil.
231 func (as *AnnotationStream) Step() *miloProto.Step { return as.cs.Step }
hinoka 2017/02/02 22:09:25 Can't you just do if as.cs.Step == nil { err :=
dnj 2017/02/02 22:29:00 Well, this way we can reload if we want. I suppose
232
233 func (as *AnnotationStream) memcacheKey() string {
234 » return fmt.Sprintf("logdog/%s/%s/%s", as.Client.Host, as.Project, as.Pat h)
235 }
236
237 func (as *AnnotationStream) toMiloBuild(c context.Context) *resp.MiloBuild {
238 » prefix, name := as.Path.Split()
254 239
255 // Prepare a Streams object with only one stream. 240 // Prepare a Streams object with only one stream.
256 streams := Streams{ 241 streams := Streams{
257 MainStream: &Stream{ 242 MainStream: &Stream{
258 » » » Server: as.host, 243 » » » Server: as.Client.Host,
259 Prefix: string(prefix), 244 Prefix: string(prefix),
260 Path: string(name), 245 Path: string(name),
261 IsDatagram: true, 246 IsDatagram: true,
262 Data: as.cs.Step, 247 Data: as.cs.Step,
263 Closed: as.cs.Finished, 248 Closed: as.cs.Finished,
264 }, 249 },
265 } 250 }
266 251
267 var ( 252 var (
268 build resp.MiloBuild 253 build resp.MiloBuild
269 ub = logDogURLBuilder{ 254 ub = logDogURLBuilder{
270 » » » project: as.project, 255 » » » host: as.Client.Host,
271 » » » host: as.host, 256 » » » project: as.Project,
272 prefix: prefix, 257 prefix: prefix,
273 } 258 }
274 ) 259 )
275 AddLogDogToBuild(c, &ub, streams.MainStream.Data, &build) 260 AddLogDogToBuild(c, &ub, streams.MainStream.Data, &build)
276 return &build 261 return &build
277 } 262 }
278 263
279 type logDogURLBuilder struct { 264 type logDogURLBuilder struct {
280 host string 265 host string
281 prefix types.StreamName 266 prefix types.StreamName
282 project cfgtypes.ProjectName 267 project cfgtypes.ProjectName
283 } 268 }
284 269
285 func (b *logDogURLBuilder) BuildLink(l *miloProto.Link) *resp.Link { 270 func (b *logDogURLBuilder) BuildLink(l *miloProto.Link) *resp.Link {
286 switch t := l.Value.(type) { 271 switch t := l.Value.(type) {
287 case *miloProto.Link_LogdogStream: 272 case *miloProto.Link_LogdogStream:
288 ls := t.LogdogStream 273 ls := t.LogdogStream
289 274
290 server := ls.Server 275 server := ls.Server
291 if server == "" { 276 if server == "" {
292 server = b.host 277 server = b.host
293 } 278 }
294 279
295 prefix := types.StreamName(ls.Prefix) 280 prefix := types.StreamName(ls.Prefix)
296 if prefix == "" { 281 if prefix == "" {
297 prefix = b.prefix 282 prefix = b.prefix
298 } 283 }
299 284
300 » » path := fmt.Sprintf("%s/%s", b.project, prefix.Join(types.Stream Name(ls.Name))) 285 » » u := viewer.GetURL(server, b.project, prefix.Join(types.StreamNa me(ls.Name)))
301 » » u := url.URL{
302 » » » Scheme: "https",
303 » » » Host: server,
304 » » » Path: "v/",
305 » » » RawQuery: url.Values{
306 » » » » "s": []string{string(path)},
307 » » » }.Encode(),
308 » » }
309
310 link := resp.Link{ 286 link := resp.Link{
311 Label: l.Label, 287 Label: l.Label,
312 » » » URL: u.String(), 288 » » » URL: u,
313 } 289 }
314 if link.Label == "" { 290 if link.Label == "" {
315 link.Label = ls.Name 291 link.Label = ls.Name
316 } 292 }
317 return &link 293 return &link
318 294
319 case *miloProto.Link_Url: 295 case *miloProto.Link_Url:
320 link := resp.Link{ 296 link := resp.Link{
321 Label: l.Label, 297 Label: l.Label,
322 URL: t.Url, 298 URL: t.Url,
323 } 299 }
324 if link.Label == "" { 300 if link.Label == "" {
325 link.Label = "unnamed" 301 link.Label = "unnamed"
326 } 302 }
327 return &link 303 return &link
328 304
329 default: 305 default:
330 // Don't know how to render. 306 // Don't know how to render.
331 return nil 307 return nil
332 } 308 }
333 } 309 }
OLDNEW
« no previous file with comments | « milo/appengine/frontend/milo.go ('k') | milo/appengine/logdog/http.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698