| OLD | NEW |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 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 swarming | 5 package swarming |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "strconv" |
| 8 "strings" | 9 "strings" |
| 10 "unicode/utf8" |
| 9 | 11 |
| 10 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1" | 12 swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1" |
| 13 "github.com/luci/luci-go/common/errors" |
| 11 "github.com/luci/luci-go/common/logging" | 14 "github.com/luci/luci-go/common/logging" |
| 12 miloProto "github.com/luci/luci-go/common/proto/milo" | 15 miloProto "github.com/luci/luci-go/common/proto/milo" |
| 13 "github.com/luci/luci-go/grpc/grpcutil" | 16 "github.com/luci/luci-go/grpc/grpcutil" |
| 14 "github.com/luci/luci-go/logdog/client/coordinator" | 17 "github.com/luci/luci-go/logdog/client/coordinator" |
| 15 "github.com/luci/luci-go/logdog/common/types" | 18 "github.com/luci/luci-go/logdog/common/types" |
| 16 "github.com/luci/luci-go/luci_config/common/cfgtypes" | 19 "github.com/luci/luci-go/luci_config/common/cfgtypes" |
| 17 milo "github.com/luci/luci-go/milo/api/proto" | 20 milo "github.com/luci/luci-go/milo/api/proto" |
| 18 "github.com/luci/luci-go/milo/appengine/logdog" | 21 "github.com/luci/luci-go/milo/appengine/logdog" |
| 19 | 22 |
| 20 "golang.org/x/net/context" | 23 "golang.org/x/net/context" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 return nil, grpcutil.NotFound | 82 return nil, grpcutil.NotFound |
| 80 } | 83 } |
| 81 | 84 |
| 82 logging.WithError(err).Errorf(c, "Failed to load Swarming task."
) | 85 logging.WithError(err).Errorf(c, "Failed to load Swarming task."
) |
| 83 return nil, grpcutil.Internal | 86 return nil, grpcutil.Internal |
| 84 } | 87 } |
| 85 | 88 |
| 86 // Determine the LogDog annotation stream path for this Swarming task. | 89 // Determine the LogDog annotation stream path for this Swarming task. |
| 87 // | 90 // |
| 88 // On failure, will return a gRPC error. | 91 // On failure, will return a gRPC error. |
| 89 » as, err := resolveLogDogAnnotations(c, fr.req, projectHint, host, req.Ta
sk) | 92 » as, err := resolveLogDogAnnotations(c, fr.req, projectHint, host, req.Ta
sk, fr.res.TryNumber) |
| 90 if err != nil { | 93 if err != nil { |
| 91 logging.WithError(err).Warningf(c, "Failed to get annotation str
eam parameters.") | 94 logging.WithError(err).Warningf(c, "Failed to get annotation str
eam parameters.") |
| 92 return nil, err | 95 return nil, err |
| 93 } | 96 } |
| 94 if err := as.Normalize(); err != nil { | 97 if err := as.Normalize(); err != nil { |
| 95 logging.WithError(err).Warningf(c, "Failed to normalize annotati
on stream parameters.") | 98 logging.WithError(err).Warningf(c, "Failed to normalize annotati
on stream parameters.") |
| 96 return nil, grpcutil.Internal | 99 return nil, grpcutil.Internal |
| 97 } | 100 } |
| 98 | 101 |
| 102 logging.Fields{ |
| 103 "project": as.Project, |
| 104 "path": as.Path, |
| 105 }.Infof(c, "Resolved annotation stream.") |
| 106 |
| 99 client, err := p.newLogdogClient(c) | 107 client, err := p.newLogdogClient(c) |
| 100 if err != nil { | 108 if err != nil { |
| 101 logging.WithError(err).Errorf(c, "Failed to create LogDog client
.") | 109 logging.WithError(err).Errorf(c, "Failed to create LogDog client
.") |
| 102 return nil, grpcutil.Internal | 110 return nil, grpcutil.Internal |
| 103 } | 111 } |
| 104 | 112 |
| 105 // Fetch LogDog annotation stream data. | 113 // Fetch LogDog annotation stream data. |
| 106 as.Client = client | 114 as.Client = client |
| 107 step, err := as.Load(c) | 115 step, err := as.Load(c) |
| 108 if err != nil { | 116 if err != nil { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 129 | 137 |
| 130 // resolveLogDogAnnotations returns a configured AnnotationStream given the inpu
t | 138 // resolveLogDogAnnotations returns a configured AnnotationStream given the inpu
t |
| 131 // parameters. | 139 // parameters. |
| 132 // | 140 // |
| 133 // This will return a gRPC error on failure. | 141 // This will return a gRPC error on failure. |
| 134 // | 142 // |
| 135 // This function is messy and implementation-specific. That's the point of this | 143 // This function is messy and implementation-specific. That's the point of this |
| 136 // endpoint, though. All of the nastiness here should be replaced with something | 144 // endpoint, though. All of the nastiness here should be replaced with something |
| 137 // more elegant once that becomes available. In the meantime... | 145 // more elegant once that becomes available. In the meantime... |
| 138 func resolveLogDogAnnotations(c context.Context, sr *swarming.SwarmingRpcsTaskRe
quest, projectHint cfgtypes.ProjectName, | 146 func resolveLogDogAnnotations(c context.Context, sr *swarming.SwarmingRpcsTaskRe
quest, projectHint cfgtypes.ProjectName, |
| 139 » host, taskID string) (*logdog.AnnotationStream, error) { | 147 » host, taskID string, tryNumber int64) (*logdog.AnnotationStream, error)
{ |
| 140 | 148 |
| 141 // If this is a Kitchen command, maybe we can infer our LogDog project f
rom | 149 // If this is a Kitchen command, maybe we can infer our LogDog project f
rom |
| 142 // the command-line. | 150 // the command-line. |
| 143 var as logdog.AnnotationStream | 151 var as logdog.AnnotationStream |
| 144 if sr.Properties == nil { | 152 if sr.Properties == nil { |
| 145 logging.Warningf(c, "No request properties, can't infer annotati
on stream path.") | 153 logging.Warningf(c, "No request properties, can't infer annotati
on stream path.") |
| 146 return nil, grpcutil.NotFound | 154 return nil, grpcutil.NotFound |
| 147 } | 155 } |
| 148 | 156 |
| 149 var isKitchen bool | 157 var isKitchen bool |
| 150 if as.Project, isKitchen = getLogDogProjectFromKitchen(sr.Properties.Com
mand); !isKitchen { | 158 if as.Project, isKitchen = getLogDogProjectFromKitchen(sr.Properties.Com
mand); !isKitchen { |
| 151 logging.Warningf(c, "Not a Kitchen CLI, can't infer annotation s
tream path.") | 159 logging.Warningf(c, "Not a Kitchen CLI, can't infer annotation s
tream path.") |
| 152 return nil, grpcutil.NotFound | 160 return nil, grpcutil.NotFound |
| 153 } | 161 } |
| 154 | 162 |
| 155 if as.Project == "" { | 163 if as.Project == "" { |
| 156 as.Project = projectHint | 164 as.Project = projectHint |
| 157 } | 165 } |
| 158 if as.Project == "" { | 166 if as.Project == "" { |
| 159 logging.Warningf(c, "Don't know how to get annotation stream pat
h.") | 167 logging.Warningf(c, "Don't know how to get annotation stream pat
h.") |
| 160 return nil, grpcutil.NotFound | 168 return nil, grpcutil.NotFound |
| 161 } | 169 } |
| 162 | 170 |
| 163 // This is a Kitchen run, and it has a project! Construct the annotation | 171 // This is a Kitchen run, and it has a project! Construct the annotation |
| 164 // stream path. | 172 // stream path. |
| 165 // | 173 // |
| 166 // This is an implementation of: | 174 // This is an implementation of: |
| 167 // https://chromium.googlesource.com/infra/infra/+/a7032e3e240d4b81a1912
bfaf29a20d02f665cc1/go/src/infra/tools/kitchen/cook_logdog.go#129 | 175 // https://chromium.googlesource.com/infra/infra/+/a7032e3e240d4b81a1912
bfaf29a20d02f665cc1/go/src/infra/tools/kitchen/cook_logdog.go#129 |
| 168 » prefix, err := types.MakeStreamName("", "swarm", host, taskID) | 176 » runID, err := getRunID(taskID, tryNumber) |
| 177 » if err != nil { |
| 178 » » logging.Fields{ |
| 179 » » » logging.ErrorKey: err, |
| 180 » » » "taskID": taskID, |
| 181 » » » "tryNumber": tryNumber, |
| 182 » » }.Errorf(c, "Failed to get Run ID for task/try.") |
| 183 » » return nil, grpcutil.Internal |
| 184 » } |
| 185 |
| 186 » prefix, err := types.MakeStreamName("", "swarm", host, runID) |
| 169 if err != nil { | 187 if err != nil { |
| 170 logging.WithError(err).Errorf(c, "Failed to generate Swarming pr
efix.") | 188 logging.WithError(err).Errorf(c, "Failed to generate Swarming pr
efix.") |
| 171 return nil, grpcutil.Internal | 189 return nil, grpcutil.Internal |
| 172 } | 190 } |
| 173 as.Path = prefix.Join("annotations") | 191 as.Path = prefix.Join("annotations") |
| 174 return &as, nil | 192 return &as, nil |
| 175 } | 193 } |
| 176 | 194 |
| 177 func getLogDogProjectFromKitchen(cmd []string) (proj cfgtypes.ProjectName, isKit
chen bool) { | 195 func getLogDogProjectFromKitchen(cmd []string) (proj cfgtypes.ProjectName, isKit
chen bool) { |
| 178 // Is this a Kitchen command? | 196 // Is this a Kitchen command? |
| (...skipping 11 matching lines...) Expand all Loading... |
| 190 if arg == "-logdog-project" { | 208 if arg == "-logdog-project" { |
| 191 if i < len(cmd)-2 { | 209 if i < len(cmd)-2 { |
| 192 proj = cfgtypes.ProjectName(cmd[i+1]) | 210 proj = cfgtypes.ProjectName(cmd[i+1]) |
| 193 return | 211 return |
| 194 } | 212 } |
| 195 break | 213 break |
| 196 } | 214 } |
| 197 } | 215 } |
| 198 return | 216 return |
| 199 } | 217 } |
| 218 |
| 219 // getRunID converts a Swarming task ID and try number into a Swarming Run ID. |
| 220 // |
| 221 // The run ID is a permutation of the last four bits of the Swarming Task ID. |
| 222 // Therefore, we chop it off of the string, mutate it, and then add it back. |
| 223 // |
| 224 // TODO(dnj): Replace this with Swarming API call once finished. |
| 225 func getRunID(taskID string, tryNumber int64) (string, error) { |
| 226 // Slice off the last character form the task ID. |
| 227 if len(taskID) == 0 { |
| 228 return "", errors.New("swarming task ID is empty") |
| 229 } |
| 230 |
| 231 // Parse "tryNumber" as a hex string. |
| 232 if tryNumber < 0 || tryNumber > 0x0F { |
| 233 return "", errors.Reason("try number %(try)d exceeds 4 bits"). |
| 234 D("try", tryNumber). |
| 235 Err() |
| 236 } |
| 237 |
| 238 lastChar, lastCharSize := utf8.DecodeLastRuneInString(taskID) |
| 239 v, err := strconv.ParseUint(string(lastChar), 16, 8) |
| 240 if err != nil { |
| 241 return "", errors.Annotate(err).Reason("failed to parse hex from
rune: %(rune)r"). |
| 242 D("rune", lastChar). |
| 243 Err() |
| 244 } |
| 245 |
| 246 return taskID[:len(taskID)-lastCharSize] + strconv.FormatUint((v|uint64(
tryNumber)), 16), nil |
| 247 } |
| OLD | NEW |