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