| OLD | NEW |
| 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 "fmt" | 8 "fmt" |
| 9 "net/http" | 9 "net/http" |
| 10 "time" | 10 "time" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 // 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 |
| 38 // query string. | 38 // query string. |
| 39 defaultLogDogHost = "luci-logdog.appspot.com" | 39 defaultLogDogHost = "luci-logdog.appspot.com" |
| 40 ) | 40 ) |
| 41 | 41 |
| 42 // AnnotationStream represents a LogDog annotation protobuf stream. | 42 // AnnotationStream represents a LogDog annotation protobuf stream. |
| 43 type AnnotationStream struct { | 43 type AnnotationStream struct { |
| 44 Project cfgtypes.ProjectName | 44 Project cfgtypes.ProjectName |
| 45 Path types.StreamPath | 45 Path types.StreamPath |
| 46 | 46 |
| 47 » // logDogClient is the HTTP client to use for LogDog communication. | 47 » // Client is the HTTP client to use for LogDog communication. |
| 48 Client *coordinator.Client | 48 Client *coordinator.Client |
| 49 | 49 |
| 50 // cs is the unmarshalled annotation stream Step and associated data. | 50 // cs is the unmarshalled annotation stream Step and associated data. |
| 51 cs internal.CachedStep | 51 cs internal.CachedStep |
| 52 } | 52 } |
| 53 | 53 |
| 54 // Normalize validates and normalizes the stream's parameters. | 54 // Normalize validates and normalizes the stream's parameters. |
| 55 func (as *AnnotationStream) Normalize() error { | 55 func (as *AnnotationStream) Normalize() error { |
| 56 if err := as.Project.Validate(); err != nil { | 56 if err := as.Project.Validate(); err != nil { |
| 57 return fmt.Errorf("Invalid project name: %s", as.Project) | 57 return fmt.Errorf("Invalid project name: %s", as.Project) |
| 58 } | 58 } |
| 59 | 59 |
| 60 if err := as.Path.Validate(); err != nil { | 60 if err := as.Path.Validate(); err != nil { |
| 61 return fmt.Errorf("Invalid log stream path %q: %s", as.Path, err
) | 61 return fmt.Errorf("Invalid log stream path %q: %s", as.Path, err
) |
| 62 } | 62 } |
| 63 | 63 |
| 64 return nil | 64 return nil |
| 65 } | 65 } |
| 66 | 66 |
| 67 // Load loads the annotation stream from LogDog. | 67 // Fetch loads the annotation stream from LogDog. |
| 68 // | 68 // |
| 69 // If the stream does not exist, or is invalid, Load will return a Milo error. | 69 // If the stream does not exist, or is invalid, Fetch will return a Milo error. |
| 70 // Otherwise, it will return the Step that was loaded. | 70 // Otherwise, it will return the Step that was loaded. |
| 71 // | 71 // |
| 72 // Load caches the step, so multiple calls to Load will return the same Step | 72 // Fetch caches the step, so multiple calls to Fetch will return the same Step |
| 73 // value. | 73 // value. |
| 74 func (as *AnnotationStream) Load(c context.Context) (*miloProto.Step, error) { | 74 func (as *AnnotationStream) Fetch(c context.Context) (*miloProto.Step, error) { |
| 75 // Cached? | 75 // Cached? |
| 76 if as.cs.Step != nil { | 76 if as.cs.Step != nil { |
| 77 return as.cs.Step, nil | 77 return as.cs.Step, nil |
| 78 } | 78 } |
| 79 | 79 |
| 80 // Load from memcache, if possible. If an error occurs, we will proceed
as if | 80 // Load from memcache, if possible. If an error occurs, we will proceed
as if |
| 81 // no CachedStep was available. | 81 // no CachedStep was available. |
| 82 mcKey := as.memcacheKey() | 82 mcKey := as.memcacheKey() |
| 83 mcItem, err := mc.GetKey(c, mcKey) | 83 mcItem, err := mc.GetKey(c, mcKey) |
| 84 switch err { | 84 switch err { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 // No substep had an ended time :( | 205 // No substep had an ended time :( |
| 206 latestEndedTime = google.TimeFromProto(step.Started) | 206 latestEndedTime = google.TimeFromProto(step.Started) |
| 207 } | 207 } |
| 208 | 208 |
| 209 // Build our CachedStep. | 209 // Build our CachedStep. |
| 210 as.cs = internal.CachedStep{ | 210 as.cs = internal.CachedStep{ |
| 211 Step: &step, | 211 Step: &step, |
| 212 Finished: (state.State.TerminalIndex >= 0 && le.StreamIndex == u
int64(state.State.TerminalIndex)), | 212 Finished: (state.State.TerminalIndex >= 0 && le.StreamIndex == u
int64(state.State.TerminalIndex)), |
| 213 } | 213 } |
| 214 | 214 |
| 215 // Annotee is apparently not putting an ended time on some annotation pr
otos. | |
| 216 // This hack will ensure that a finished build will always have an ended
time. | |
| 217 if as.cs.Finished && as.cs.Step.Ended == nil { | |
| 218 as.cs.Step.Ended = google.NewTimestamp(latestEndedTime) | |
| 219 } | |
| 220 | |
| 221 // Marshal and cache the step. If this is the final protobuf in the stre
am, | 215 // Marshal and cache the step. If this is the final protobuf in the stre
am, |
| 222 // cache it indefinitely; otherwise, cache it for intermediateCacheLifet
ime. | 216 // cache it indefinitely; otherwise, cache it for intermediateCacheLifet
ime. |
| 223 // | 217 // |
| 224 // If this fails, it is non-fatal. | 218 // If this fails, it is non-fatal. |
| 225 mcData, err := proto.Marshal(&as.cs) | 219 mcData, err := proto.Marshal(&as.cs) |
| 226 if err == nil { | 220 if err == nil { |
| 227 mcItem = mc.NewItem(c, mcKey) | 221 mcItem = mc.NewItem(c, mcKey) |
| 228 if !as.cs.Finished { | 222 if !as.cs.Finished { |
| 229 mcItem.SetExpiration(intermediateCacheLifetime) | 223 mcItem.SetExpiration(intermediateCacheLifetime) |
| 230 } | 224 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 253 Prefix: string(prefix), | 247 Prefix: string(prefix), |
| 254 Path: string(name), | 248 Path: string(name), |
| 255 IsDatagram: true, | 249 IsDatagram: true, |
| 256 Data: as.cs.Step, | 250 Data: as.cs.Step, |
| 257 Closed: as.cs.Finished, | 251 Closed: as.cs.Finished, |
| 258 }, | 252 }, |
| 259 } | 253 } |
| 260 | 254 |
| 261 var ( | 255 var ( |
| 262 build resp.MiloBuild | 256 build resp.MiloBuild |
| 263 » » ub = logDogURLBuilder{ | 257 » » ub = ViewerURLBuilder{ |
| 264 » » » host: as.Client.Host, | 258 » » » Host: as.Client.Host, |
| 265 » » » project: as.Project, | 259 » » » Project: as.Project, |
| 266 » » » prefix: prefix, | 260 » » » Prefix: prefix, |
| 267 } | 261 } |
| 268 ) | 262 ) |
| 269 AddLogDogToBuild(c, &ub, streams.MainStream.Data, &build) | 263 AddLogDogToBuild(c, &ub, streams.MainStream.Data, &build) |
| 270 return &build | 264 return &build |
| 271 } | 265 } |
| 272 | 266 |
| 273 type logDogURLBuilder struct { | 267 // ViewerURLBuilder is a URL builder that constructs LogDog viewer URLs. |
| 274 » host string | 268 type ViewerURLBuilder struct { |
| 275 » prefix types.StreamName | 269 » Host string |
| 276 » project cfgtypes.ProjectName | 270 » Prefix types.StreamName |
| 271 » Project cfgtypes.ProjectName |
| 277 } | 272 } |
| 278 | 273 |
| 279 func (b *logDogURLBuilder) BuildLink(l *miloProto.Link) *resp.Link { | 274 // BuildLink implements URLBuilder. |
| 275 func (b *ViewerURLBuilder) BuildLink(l *miloProto.Link) *resp.Link { |
| 280 switch t := l.Value.(type) { | 276 switch t := l.Value.(type) { |
| 281 case *miloProto.Link_LogdogStream: | 277 case *miloProto.Link_LogdogStream: |
| 282 ls := t.LogdogStream | 278 ls := t.LogdogStream |
| 283 | 279 |
| 284 server := ls.Server | 280 server := ls.Server |
| 285 if server == "" { | 281 if server == "" { |
| 286 » » » server = b.host | 282 » » » server = b.Host |
| 287 } | 283 } |
| 288 | 284 |
| 289 prefix := types.StreamName(ls.Prefix) | 285 prefix := types.StreamName(ls.Prefix) |
| 290 if prefix == "" { | 286 if prefix == "" { |
| 291 » » » prefix = b.prefix | 287 » » » prefix = b.Prefix |
| 292 } | 288 } |
| 293 | 289 |
| 294 » » u := viewer.GetURL(server, b.project, prefix.Join(types.StreamNa
me(ls.Name))) | 290 » » u := viewer.GetURL(server, b.Project, prefix.Join(types.StreamNa
me(ls.Name))) |
| 295 link := resp.Link{ | 291 link := resp.Link{ |
| 296 Label: l.Label, | 292 Label: l.Label, |
| 297 URL: u, | 293 URL: u, |
| 298 } | 294 } |
| 299 if link.Label == "" { | 295 if link.Label == "" { |
| 300 link.Label = ls.Name | 296 link.Label = ls.Name |
| 301 } | 297 } |
| 302 return &link | 298 return &link |
| 303 | 299 |
| 304 case *miloProto.Link_Url: | 300 case *miloProto.Link_Url: |
| 305 link := resp.Link{ | 301 link := resp.Link{ |
| 306 Label: l.Label, | 302 Label: l.Label, |
| 307 URL: t.Url, | 303 URL: t.Url, |
| 308 } | 304 } |
| 309 if link.Label == "" { | 305 if link.Label == "" { |
| 310 link.Label = "unnamed" | 306 link.Label = "unnamed" |
| 311 } | 307 } |
| 312 return &link | 308 return &link |
| 313 | 309 |
| 314 default: | 310 default: |
| 315 // Don't know how to render. | 311 // Don't know how to render. |
| 316 return nil | 312 return nil |
| 317 } | 313 } |
| 318 } | 314 } |
| OLD | NEW |