| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package logdog | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "time" | |
| 10 | |
| 11 "golang.org/x/net/context" | |
| 12 | |
| 13 "github.com/luci/luci-go/common/clock" | |
| 14 "github.com/luci/luci-go/common/proto/google" | |
| 15 miloProto "github.com/luci/luci-go/common/proto/milo" | |
| 16 "github.com/luci/luci-go/milo/api/resp" | |
| 17 "github.com/luci/luci-go/milo/appengine/common/model" | |
| 18 ) | |
| 19 | |
| 20 // URLBuilder constructs URLs for various link types. | |
| 21 type URLBuilder interface { | |
| 22 // LinkURL returns the URL associated with the supplied Link. | |
| 23 // | |
| 24 // If no URL could be built for that Link, nil will be returned. | |
| 25 BuildLink(l *miloProto.Link) *resp.Link | |
| 26 } | |
| 27 | |
| 28 // HACK(hinoka): This should be a part of recipes, but just hardcoding a list | |
| 29 // of unimportant things for now. | |
| 30 var builtIn = map[string]struct{}{ | |
| 31 "recipe bootstrap": {}, | |
| 32 "setup_build": {}, | |
| 33 "recipe result": {}, | |
| 34 } | |
| 35 | |
| 36 // miloBuildStep converts a logdog/milo step to a BuildComponent struct. | |
| 37 // buildCompletedTime must be zero if build did not complete yet. | |
| 38 func miloBuildStep(ub URLBuilder, anno *miloProto.Step, isMain bool, buildComple
tedTime, | |
| 39 now time.Time) []*resp.BuildComponent { | |
| 40 | |
| 41 comp := &resp.BuildComponent{Label: anno.Name} | |
| 42 switch anno.Status { | |
| 43 case miloProto.Status_RUNNING: | |
| 44 comp.Status = model.Running | |
| 45 | |
| 46 case miloProto.Status_SUCCESS: | |
| 47 comp.Status = model.Success | |
| 48 | |
| 49 case miloProto.Status_FAILURE: | |
| 50 if fd := anno.GetFailureDetails(); fd != nil { | |
| 51 switch fd.Type { | |
| 52 case miloProto.FailureDetails_EXCEPTION, miloProto.Failu
reDetails_INFRA: | |
| 53 comp.Status = model.InfraFailure | |
| 54 | |
| 55 case miloProto.FailureDetails_EXPIRED: | |
| 56 comp.Status = model.Expired | |
| 57 | |
| 58 case miloProto.FailureDetails_DM_DEPENDENCY_FAILED: | |
| 59 comp.Status = model.DependencyFailure | |
| 60 | |
| 61 default: | |
| 62 comp.Status = model.Failure | |
| 63 } | |
| 64 | |
| 65 if fd.Text != "" { | |
| 66 comp.Text = append(comp.Text, fd.Text) | |
| 67 } | |
| 68 } else { | |
| 69 comp.Status = model.Failure | |
| 70 } | |
| 71 | |
| 72 case miloProto.Status_PENDING: | |
| 73 comp.Status = model.NotRun | |
| 74 | |
| 75 // Missing the case of waiting on unfinished dependency... | |
| 76 default: | |
| 77 comp.Status = model.NotRun | |
| 78 } | |
| 79 | |
| 80 if !(buildCompletedTime.IsZero() || comp.Status.Terminal()) { | |
| 81 // The build has completed, but this step has not. Mark it as an | |
| 82 // infrastructure failure. | |
| 83 comp.Status = model.InfraFailure | |
| 84 } | |
| 85 | |
| 86 // Hide the unimportant steps, highlight the interesting ones. | |
| 87 switch comp.Status { | |
| 88 case model.NotRun, model.Running: | |
| 89 if isMain { | |
| 90 comp.Verbosity = resp.Hidden | |
| 91 } | |
| 92 | |
| 93 case model.Success: | |
| 94 if _, ok := builtIn[anno.Name]; ok || isMain { | |
| 95 comp.Verbosity = resp.Hidden | |
| 96 } | |
| 97 case model.InfraFailure, model.Failure: | |
| 98 comp.Verbosity = resp.Interesting | |
| 99 } | |
| 100 | |
| 101 // Main link is a link to the stdout. | |
| 102 var stdoutLink *miloProto.Link | |
| 103 if anno.StdoutStream != nil { | |
| 104 stdoutLink = &miloProto.Link{ | |
| 105 Label: "stdout", | |
| 106 Value: &miloProto.Link_LogdogStream{ | |
| 107 LogdogStream: anno.StdoutStream, | |
| 108 }, | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 if anno.Link != nil { | |
| 113 comp.MainLink = resp.LinkSet{ub.BuildLink(anno.Link)} | |
| 114 | |
| 115 // If we also have a STDOUT stream, add it to our OtherLinks. | |
| 116 if stdoutLink != nil { | |
| 117 anno.OtherLinks = append([]*miloProto.Link{stdoutLink},
anno.OtherLinks...) | |
| 118 } | |
| 119 } else if stdoutLink != nil { | |
| 120 comp.MainLink = resp.LinkSet{ub.BuildLink(stdoutLink)} | |
| 121 } | |
| 122 | |
| 123 // Add STDERR link, if available. | |
| 124 if anno.StderrStream != nil { | |
| 125 anno.OtherLinks = append(anno.OtherLinks, &miloProto.Link{ | |
| 126 Label: "stderr", | |
| 127 Value: &miloProto.Link_LogdogStream{ | |
| 128 LogdogStream: anno.StderrStream, | |
| 129 }, | |
| 130 }) | |
| 131 } | |
| 132 | |
| 133 // Sub link is for one link per log that isn't stdout. | |
| 134 for _, link := range anno.GetOtherLinks() { | |
| 135 if l := ub.BuildLink(link); l != nil { | |
| 136 comp.SubLink = append(comp.SubLink, resp.LinkSet{l}) | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 // This should always be a step. | |
| 141 comp.Type = resp.Step | |
| 142 | |
| 143 // This should always be 0 | |
| 144 comp.LevelsDeep = 0 | |
| 145 | |
| 146 // Timestamps | |
| 147 comp.Started = google.TimeFromProto(anno.Started) | |
| 148 comp.Finished = google.TimeFromProto(anno.Ended) | |
| 149 | |
| 150 var till time.Time | |
| 151 switch { | |
| 152 case !comp.Finished.IsZero(): | |
| 153 till = comp.Finished | |
| 154 case comp.Status == model.Running: | |
| 155 till = now | |
| 156 case !buildCompletedTime.IsZero(): | |
| 157 till = buildCompletedTime | |
| 158 } | |
| 159 if !comp.Started.IsZero() && till.After(comp.Started) { | |
| 160 comp.Duration = till.Sub(comp.Started) | |
| 161 } | |
| 162 | |
| 163 // This should be the exact same thing. | |
| 164 comp.Text = append(comp.Text, anno.Text...) | |
| 165 | |
| 166 ss := anno.GetSubstep() | |
| 167 results := []*resp.BuildComponent{} | |
| 168 results = append(results, comp) | |
| 169 // Process nested steps. | |
| 170 for _, substep := range ss { | |
| 171 var subanno *miloProto.Step | |
| 172 switch s := substep.GetSubstep().(type) { | |
| 173 case *miloProto.Step_Substep_Step: | |
| 174 subanno = s.Step | |
| 175 case *miloProto.Step_Substep_AnnotationStream: | |
| 176 panic("Non-inline substeps not supported") | |
| 177 default: | |
| 178 panic(fmt.Errorf("Unknown type %v", s)) | |
| 179 } | |
| 180 for _, subcomp := range miloBuildStep(ub, subanno, false, buildC
ompletedTime, now) { | |
| 181 results = append(results, subcomp) | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 return results | |
| 186 } | |
| 187 | |
| 188 // AddLogDogToBuild takes a set of logdog streams and populate a milo build. | |
| 189 // build.Summary.Finished must be set. | |
| 190 func AddLogDogToBuild( | |
| 191 c context.Context, ub URLBuilder, mainAnno *miloProto.Step, build *resp.
MiloBuild) { | |
| 192 now := clock.Now(c) | |
| 193 | |
| 194 // Now fill in each of the step components. | |
| 195 // TODO(hinoka): This is totes cachable. | |
| 196 buildCompletedTime := google.TimeFromProto(mainAnno.Ended) | |
| 197 build.Summary = *(miloBuildStep(ub, mainAnno, true, buildCompletedTime,
now)[0]) | |
| 198 for _, substepContainer := range mainAnno.Substep { | |
| 199 anno := substepContainer.GetStep() | |
| 200 if anno == nil { | |
| 201 // TODO: We ignore non-embedded substeps for now. | |
| 202 continue | |
| 203 } | |
| 204 | |
| 205 bss := miloBuildStep(ub, anno, false, buildCompletedTime, now) | |
| 206 for _, bs := range bss { | |
| 207 if bs.Status != model.Success { | |
| 208 build.Summary.Text = append( | |
| 209 build.Summary.Text, fmt.Sprintf("%s %s",
bs.Status, bs.Label)) | |
| 210 } | |
| 211 build.Components = append(build.Components, bs) | |
| 212 propGroup := &resp.PropertyGroup{GroupName: bs.Label} | |
| 213 for _, prop := range anno.Property { | |
| 214 propGroup.Property = append(propGroup.Property,
&resp.Property{ | |
| 215 Key: prop.Name, | |
| 216 Value: prop.Value, | |
| 217 }) | |
| 218 } | |
| 219 build.PropertyGroup = append(build.PropertyGroup, propGr
oup) | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 // Take care of properties | |
| 224 propGroup := &resp.PropertyGroup{GroupName: "Main"} | |
| 225 for _, prop := range mainAnno.Property { | |
| 226 propGroup.Property = append(propGroup.Property, &resp.Property{ | |
| 227 Key: prop.Name, | |
| 228 Value: prop.Value, | |
| 229 }) | |
| 230 } | |
| 231 build.PropertyGroup = append(build.PropertyGroup, propGroup) | |
| 232 | |
| 233 return | |
| 234 } | |
| OLD | NEW |