Chromium Code Reviews| 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 "strings" | |
| 10 "time" | 9 "time" |
| 11 | 10 |
| 12 "golang.org/x/net/context" | 11 "golang.org/x/net/context" |
| 13 | 12 |
| 14 "github.com/luci/luci-go/appengine/cmd/milo/resp" | 13 "github.com/luci/luci-go/appengine/cmd/milo/resp" |
| 15 "github.com/luci/luci-go/common/clock" | 14 "github.com/luci/luci-go/common/clock" |
| 16 "github.com/luci/luci-go/common/logging" | |
| 17 miloProto "github.com/luci/luci-go/common/proto/milo" | 15 miloProto "github.com/luci/luci-go/common/proto/milo" |
| 18 ) | 16 ) |
| 19 | 17 |
| 18 // URLBuilder constructs URLs for various link types. | |
| 19 type URLBuilder interface { | |
| 20 // LinkURL returns the URL associated with the supplied Link. | |
| 21 // | |
| 22 // If no URL could be built for that Link, nil will be returned. | |
| 23 BuildLink(l *miloProto.Link) *resp.Link | |
| 24 } | |
| 25 | |
| 20 // miloBuildStep converts a logdog/milo step to a BuildComponent struct. | 26 // miloBuildStep converts a logdog/milo step to a BuildComponent struct. |
| 21 // buildCompletedTime must be zero if build did not complete yet. | 27 // buildCompletedTime must be zero if build did not complete yet. |
| 22 func miloBuildStep(c context.Context, linkBase string, anno *miloProto.Step, bui ldCompletedTime time.Time) *resp.BuildComponent { | 28 func miloBuildStep(c context.Context, ub URLBuilder, anno *miloProto.Step, now t ime.Time) *resp.BuildComponent { |
| 23 » linkBase = strings.TrimSuffix(linkBase, "/") | |
| 24 comp := &resp.BuildComponent{Label: anno.Name} | 29 comp := &resp.BuildComponent{Label: anno.Name} |
| 25 switch anno.Status { | 30 switch anno.Status { |
| 26 case miloProto.Status_RUNNING: | 31 case miloProto.Status_RUNNING: |
| 27 comp.Status = resp.Running | 32 comp.Status = resp.Running |
| 28 | 33 |
| 29 case miloProto.Status_SUCCESS: | 34 case miloProto.Status_SUCCESS: |
| 30 comp.Status = resp.Success | 35 comp.Status = resp.Success |
| 31 | 36 |
| 32 case miloProto.Status_FAILURE: | 37 case miloProto.Status_FAILURE: |
| 33 » » if anno.GetFailureDetails() != nil { | 38 » » if fd := anno.GetFailureDetails(); fd != nil { |
| 34 » » » switch anno.GetFailureDetails().Type { | 39 » » » switch fd.Type { |
| 35 case miloProto.FailureDetails_EXCEPTION, miloProto.Failu reDetails_INFRA: | 40 case miloProto.FailureDetails_EXCEPTION, miloProto.Failu reDetails_INFRA: |
| 36 comp.Status = resp.InfraFailure | 41 comp.Status = resp.InfraFailure |
| 37 | 42 |
| 38 case miloProto.FailureDetails_DM_DEPENDENCY_FAILED: | 43 case miloProto.FailureDetails_DM_DEPENDENCY_FAILED: |
| 39 comp.Status = resp.DependencyFailure | 44 comp.Status = resp.DependencyFailure |
| 40 | 45 |
| 41 default: | 46 default: |
| 42 comp.Status = resp.Failure | 47 comp.Status = resp.Failure |
| 43 } | 48 } |
| 49 | |
| 50 if fd.Text != "" { | |
| 51 comp.Text = append(comp.Text, fd.Text) | |
| 52 } | |
| 44 } else { | 53 } else { |
| 45 comp.Status = resp.Failure | 54 comp.Status = resp.Failure |
| 46 } | 55 } |
| 47 | 56 |
| 57 case miloProto.Status_PENDING: | |
| 58 comp.Status = resp.NotRun | |
| 59 | |
| 48 // Missing the case of waiting on unfinished dependency... | 60 // Missing the case of waiting on unfinished dependency... |
| 49 default: | 61 default: |
| 50 comp.Status = resp.NotRun | 62 comp.Status = resp.NotRun |
| 51 } | 63 } |
| 52 | 64 |
| 53 » if !buildCompletedTime.IsZero() && !comp.Status.Terminal() { | 65 » if !anno.Ended.Time().IsZero() && !comp.Status.Terminal() { |
| 54 // we cannot have unfinished steps in finished builds. | 66 // we cannot have unfinished steps in finished builds. |
| 55 comp.Status = resp.InfraFailure | 67 comp.Status = resp.InfraFailure |
| 56 } | 68 } |
| 57 | 69 |
| 58 // Sub link is for one link per log that isn't stdout. | |
| 59 for _, link := range anno.GetOtherLinks() { | |
| 60 lds := link.GetLogdogStream() | |
| 61 if lds == nil { | |
| 62 logging.Warningf(c, "Warning: %v of %v has an empty logd og stream.", link, anno) | |
| 63 continue // DNE??? | |
| 64 } | |
| 65 newLink := &resp.Link{ | |
| 66 Label: lds.Name, | |
| 67 URL: linkBase + "/" + lds.Name, | |
| 68 } | |
| 69 comp.SubLink = append(comp.SubLink, newLink) | |
| 70 } | |
| 71 | |
| 72 // Main link is a link to the stdout. | 70 // Main link is a link to the stdout. |
| 71 var stdoutLink *miloProto.Link | |
| 73 if anno.StdoutStream != nil { | 72 if anno.StdoutStream != nil { |
| 74 » » comp.MainLink = &resp.Link{ | 73 » » stdoutLink = &miloProto.Link{ |
| 75 Label: "stdout", | 74 Label: "stdout", |
| 76 » » » URL: linkBase + "/" + anno.StdoutStream.Name, | 75 » » » Value: &miloProto.Link_LogdogStream{ |
| 76 » » » » LogdogStream: anno.StdoutStream, | |
| 77 » » » }, | |
| 77 } | 78 } |
| 78 } | 79 } |
| 79 | 80 |
| 81 if anno.Link != nil { | |
| 82 comp.MainLink = ub.BuildLink(anno.Link) | |
| 83 | |
| 84 // If we also have a STDOUT stream, add it to our OtherLinks. | |
| 85 if stdoutLink != nil { | |
| 86 anno.OtherLinks = append([]*miloProto.Link{stdoutLink}, anno.OtherLinks...) | |
| 87 } | |
| 88 } else if stdoutLink != nil { | |
| 89 comp.MainLink = ub.BuildLink(stdoutLink) | |
| 90 } | |
| 91 | |
| 92 // Sub link is for one link per log that isn't stdout. | |
| 93 for _, link := range anno.GetOtherLinks() { | |
| 94 if l := ub.BuildLink(link); l != nil { | |
| 95 comp.SubLink = append(comp.SubLink, l) | |
| 96 } | |
| 97 } | |
| 98 | |
| 80 // This should always be a step. | 99 // This should always be a step. |
| 81 comp.Type = resp.Step | 100 comp.Type = resp.Step |
| 82 | 101 |
| 83 // This should always be 0 | 102 // This should always be 0 |
| 84 comp.LevelsDeep = 0 | 103 comp.LevelsDeep = 0 |
| 85 | 104 |
| 86 // Timestamps | 105 // Timestamps |
| 87 comp.Started = anno.Started.Time() | 106 comp.Started = anno.Started.Time() |
| 88 comp.Finished = anno.Ended.Time() | 107 comp.Finished = anno.Ended.Time() |
| 89 | 108 |
| 109 latestTime := comp.Finished | |
| 110 if latestTime.IsZero() { | |
| 111 latestTime = now | |
|
nodir
2016/07/29 18:42:06
it should not be now, but time of build completion
dnj
2016/07/29 19:57:35
Done.
| |
| 112 } | |
| 113 if !comp.Started.IsZero() { | |
| 114 if d := latestTime.Sub(comp.Started); d >= 0 { | |
| 115 comp.Duration = d | |
| 116 } | |
| 117 } | |
| 118 | |
|
nodir
2016/07/29 18:42:06
this block of added code solves the same problem a
dnj
2016/07/29 19:57:35
Acknowledged.
| |
| 90 var till time.Time | 119 var till time.Time |
| 91 switch { | 120 switch { |
| 92 case comp.Status == resp.Running: | 121 case comp.Status == resp.Running: |
| 93 till = clock.Now(c) | 122 till = clock.Now(c) |
| 94 case !comp.Finished.IsZero(): | 123 case !comp.Finished.IsZero(): |
| 95 till = comp.Finished | 124 till = comp.Finished |
| 96 default: | |
| 97 till = buildCompletedTime | |
| 98 } | 125 } |
| 99 if !comp.Started.IsZero() && !till.IsZero() { | 126 if !comp.Started.IsZero() && !till.IsZero() { |
| 100 comp.Duration = till.Sub(comp.Started) | 127 comp.Duration = till.Sub(comp.Started) |
| 101 } | 128 } |
| 102 | 129 |
| 103 // This should be the exact same thing. | 130 // This should be the exact same thing. |
| 104 » comp.Text = anno.Text | 131 » comp.Text = append(comp.Text, anno.Text...) |
| 105 | 132 |
| 106 return comp | 133 return comp |
| 107 } | 134 } |
| 108 | 135 |
| 109 // AddLogDogToBuild takes a set of logdog streams and populate a milo build. | 136 // AddLogDogToBuild takes a set of logdog streams and populate a milo build. |
| 110 // build.Summary.Finished must be set. | 137 // build.Summary.Finished must be set. |
| 111 func AddLogDogToBuild(c context.Context, linkBase string, s *Streams, build *res p.MiloBuild) { | 138 func AddLogDogToBuild(c context.Context, ub URLBuilder, s *Streams, build *resp. MiloBuild) { |
| 112 if s.MainStream == nil { | 139 if s.MainStream == nil { |
| 113 panic("missing main stream") | 140 panic("missing main stream") |
| 114 } | 141 } |
| 115 // Now Fetch the main annotation of the build. | 142 // Now Fetch the main annotation of the build. |
| 116 » mainAnno := s.MainStream.Data | 143 » var ( |
| 144 » » mainAnno = s.MainStream.Data | |
| 145 » » now = clock.Now(c) | |
| 146 » ) | |
| 117 | 147 |
| 118 // Now fill in each of the step components. | 148 // Now fill in each of the step components. |
| 119 // TODO(hinoka): This is totes cachable. | 149 // TODO(hinoka): This is totes cachable. |
| 150 build.Summary = *(miloBuildStep(c, ub, mainAnno, now)) | |
| 120 for _, substepContainer := range mainAnno.Substep { | 151 for _, substepContainer := range mainAnno.Substep { |
| 121 anno := substepContainer.GetStep() | 152 anno := substepContainer.GetStep() |
| 122 if anno == nil { | 153 if anno == nil { |
| 123 // TODO: We ignore non-embedded substeps for now. | 154 // TODO: We ignore non-embedded substeps for now. |
| 124 continue | 155 continue |
| 125 } | 156 } |
| 126 | 157 |
| 127 » » bs := miloBuildStep(c, linkBase, anno, build.Summary.Finished) | 158 » » bs := miloBuildStep(c, ub, anno, now) |
| 128 » » if bs.Status != resp.Success && bs.Status != resp.NotRun { | 159 » » if bs.Status != resp.Success { |
| 129 build.Summary.Text = append( | 160 build.Summary.Text = append( |
| 130 build.Summary.Text, fmt.Sprintf("%s %s", bs.Stat us, bs.Label)) | 161 build.Summary.Text, fmt.Sprintf("%s %s", bs.Stat us, bs.Label)) |
| 131 } | 162 } |
| 132 build.Components = append(build.Components, bs) | 163 build.Components = append(build.Components, bs) |
| 133 propGroup := &resp.PropertyGroup{GroupName: bs.Label} | 164 propGroup := &resp.PropertyGroup{GroupName: bs.Label} |
| 134 for _, prop := range anno.Property { | 165 for _, prop := range anno.Property { |
| 135 propGroup.Property = append(propGroup.Property, &resp.Pr operty{ | 166 propGroup.Property = append(propGroup.Property, &resp.Pr operty{ |
| 136 Key: prop.Name, | 167 Key: prop.Name, |
| 137 Value: prop.Value, | 168 Value: prop.Value, |
| 138 }) | 169 }) |
| 139 } | 170 } |
| 140 build.PropertyGroup = append(build.PropertyGroup, propGroup) | 171 build.PropertyGroup = append(build.PropertyGroup, propGroup) |
| 141 } | 172 } |
| 142 | 173 |
| 143 // Take care of properties | 174 // Take care of properties |
| 144 propGroup := &resp.PropertyGroup{GroupName: "Main"} | 175 propGroup := &resp.PropertyGroup{GroupName: "Main"} |
| 145 for _, prop := range mainAnno.Property { | 176 for _, prop := range mainAnno.Property { |
| 146 propGroup.Property = append(propGroup.Property, &resp.Property{ | 177 propGroup.Property = append(propGroup.Property, &resp.Property{ |
| 147 Key: prop.Name, | 178 Key: prop.Name, |
| 148 Value: prop.Value, | 179 Value: prop.Value, |
| 149 }) | 180 }) |
| 150 } | 181 } |
| 151 build.PropertyGroup = append(build.PropertyGroup, propGroup) | 182 build.PropertyGroup = append(build.PropertyGroup, propGroup) |
| 152 | 183 |
| 153 return | 184 return |
| 154 } | 185 } |
| OLD | NEW |