Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 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 "bytes" | 8 "bytes" |
| 9 "fmt" | 9 "fmt" |
| 10 "net/http" | 10 "net/http" |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 } | 240 } |
| 241 if sr.State == TaskCompleted || sr.State == TaskTimedOut { | 241 if sr.State == TaskCompleted || sr.State == TaskTimedOut { |
| 242 props.Property = append(props.Property, &resp.Property{ | 242 props.Property = append(props.Property, &resp.Property{ |
| 243 Key: "Exit Code", | 243 Key: "Exit Code", |
| 244 Value: fmt.Sprintf("%d", sr.ExitCode), | 244 Value: fmt.Sprintf("%d", sr.ExitCode), |
| 245 }) | 245 }) |
| 246 } | 246 } |
| 247 return props | 247 return props |
| 248 } | 248 } |
| 249 | 249 |
| 250 func tagsToProperties(tags []string) *resp.PropertyGroup { | 250 func tagsToMap(tags []string) map[string]string { |
| 251 » props := &resp.PropertyGroup{GroupName: "Swarming Tags"} | 251 » result := make(map[string]string, len(tags)) |
| 252 for _, t := range tags { | 252 for _, t := range tags { |
| 253 » » if t == "" { | 253 » » parts := strings.SplitN(t, ":", 2) |
| 254 » » » continue | 254 » » if len(parts) == 2 { |
| 255 » » » result[parts[0]] = parts[1] | |
| 255 } | 256 } |
| 256 parts := strings.SplitN(t, ":", 2) | |
| 257 p := &resp.Property{ | |
| 258 Key: parts[0], | |
| 259 } | |
| 260 if len(parts) == 2 { | |
| 261 p.Value = parts[1] | |
| 262 } | |
| 263 props.Property = append(props.Property, p) | |
| 264 } | 257 } |
| 265 » return props | 258 » return result |
| 266 } | 259 } |
| 267 | 260 |
| 268 // addBuilderLink adds a link to the buildbucket builder view. | 261 // addBuilderLink adds a link to the buildbucket builder view. |
| 269 func addBuilderLink(c context.Context, build *resp.MiloBuild, swarmingHostname s tring, sr *swarming.SwarmingRpcsTaskResult) { | 262 func addBuilderLink(c context.Context, build *resp.MiloBuild, tags map[string]st ring) { |
| 270 » tags := swarmingTags(sr.Tags) | |
| 271 | |
| 272 bbHost := tags["buildbucket_hostname"] | 263 bbHost := tags["buildbucket_hostname"] |
| 273 bucket := tags["buildbucket_bucket"] | 264 bucket := tags["buildbucket_bucket"] |
| 274 builder := tags["builder"] | 265 builder := tags["builder"] |
| 275 if bucket == "" { | |
| 276 logging.Errorf( | |
| 277 c, "Could not extract buildbucket bucket from task %s", | |
| 278 taskPageURL(swarmingHostname, sr.TaskId)) | |
| 279 } | |
| 280 if builder == "" { | |
| 281 logging.Errorf( | |
| 282 c, "Could not extract builder name from task %s", | |
| 283 taskPageURL(swarmingHostname, sr.TaskId)) | |
| 284 } | |
| 285 if bucket != "" && builder != "" { | 266 if bucket != "" && builder != "" { |
| 286 build.Summary.ParentLabel = &resp.Link{ | 267 build.Summary.ParentLabel = &resp.Link{ |
| 287 Label: builder, | 268 Label: builder, |
| 288 URL: fmt.Sprintf("/buildbucket/%s/%s?server=%s", bucke t, builder, bbHost), | 269 URL: fmt.Sprintf("/buildbucket/%s/%s?server=%s", bucke t, builder, bbHost), |
| 289 } | 270 } |
| 290 } | 271 } |
| 291 } | 272 } |
| 292 | 273 |
| 293 // addBanner adds an OS banner derived from "os" swarming tag, if present. | 274 // addBanner adds an OS banner derived from "os" swarming tag, if present. |
| 294 func addBanner(build *resp.MiloBuild, sr *swarming.SwarmingRpcsTaskResult) { | 275 func addBanner(build *resp.MiloBuild, tags map[string]string) { |
| 295 » var os, ver string | 276 » os := tags["os"] |
| 296 » for _, t := range sr.Tags { | 277 » var ver string |
| 297 » » value := strings.TrimPrefix(t, "os:") | 278 » parts := strings.SplitN(os, "-", 2) |
| 298 » » if value == t { | 279 » if len(parts) == 2 { |
| 299 » » » // t does not have the prefix | 280 » » os = parts[0] |
| 300 » » » continue | 281 » » ver = parts[1] |
| 301 » » } | |
| 302 » » parts := strings.SplitN(value, "-", 2) | |
| 303 » » if len(parts) == 2 { | |
| 304 » » » os = parts[0] | |
| 305 » » » ver = parts[1] | |
| 306 » » » break | |
| 307 » » } | |
| 308 } | 282 } |
| 309 | 283 |
| 310 var base resp.LogoBase | 284 var base resp.LogoBase |
| 311 switch os { | 285 switch os { |
| 312 case "Ubuntu": | 286 case "Ubuntu": |
| 313 base = resp.Ubuntu | 287 base = resp.Ubuntu |
| 314 case "Windows": | 288 case "Windows": |
| 315 base = resp.Windows | 289 base = resp.Windows |
| 316 case "Mac": | 290 case "Mac": |
| 317 base = resp.OSX | 291 base = resp.OSX |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 408 ts, err := time.Parse(SwarmingTimeLayout, sr.CompletedTs) | 382 ts, err := time.Parse(SwarmingTimeLayout, sr.CompletedTs) |
| 409 if err != nil { | 383 if err != nil { |
| 410 return fmt.Errorf("invalid task CompletedTs: %s", err) | 384 return fmt.Errorf("invalid task CompletedTs: %s", err) |
| 411 } | 385 } |
| 412 step.Ended = google.NewTimestamp(ts) | 386 step.Ended = google.NewTimestamp(ts) |
| 413 } | 387 } |
| 414 | 388 |
| 415 return nil | 389 return nil |
| 416 } | 390 } |
| 417 | 391 |
| 392 func addBuildsetInfo(build *resp.MiloBuild, tags map[string]string) { | |
| 393 buildset := tags["buildset"] | |
| 394 if !strings.HasPrefix(buildset, "patch/") { | |
| 395 // Buildset isn't a patch, ignore. | |
| 396 return | |
| 397 } | |
| 398 | |
| 399 patchset := strings.TrimLeft(buildset, "patch/") | |
| 400 // TODO(hinoka): Also support Rietveld patches. | |
| 401 if strings.HasPrefix(patchset, "gerrit/") { | |
| 402 gerritPatchset := strings.TrimLeft(patchset, "gerrit/") | |
| 403 parts := strings.Split(gerritPatchset, "/") | |
| 404 if len(parts) != 3 { | |
| 405 // Not a well-formed gerrit patchset. | |
| 406 return | |
| 407 } | |
| 408 if build.SourceStamp == nil { | |
| 409 build.SourceStamp = &resp.SourceStamp{} | |
| 410 } | |
| 411 build.SourceStamp.Changelist = &resp.Link{ | |
| 412 Label: "Gerrit CL", | |
| 413 URL: fmt.Sprintf("https://%s/c/%s/%s", parts[0], parts [1], parts[2]), | |
| 414 } | |
| 415 | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 func addRecipeLink(build *resp.MiloBuild, tags map[string]string) { | |
| 420 name := tags["recipe_name"] | |
| 421 repoURL := tags["recipe_repository"] | |
| 422 revision := tags["recipe_revision"] | |
| 423 if name != "" && repoURL != "" { | |
| 424 if revision == "" { | |
| 425 revision = "master" | |
| 426 } | |
| 427 // Link directly to the revision if it is a gerrit URL, otherwis e just | |
| 428 // display it in the name. | |
| 429 if repoParse, err := url.Parse(repoURL); err != nil && strings.H asSuffix( | |
|
nodir
2017/03/02 04:01:25
err == nil
do you check your expectation files? :
| |
| 430 repoParse.Host, "googlesource.com") { | |
|
nodir
2017/03/02 04:01:25
prepend dot to googlesource.com
| |
| 431 repoURL += "/+/" + revision + "/" | |
| 432 } else { | |
| 433 if len(revision) > 8 { | |
| 434 revision = revision[:8] | |
| 435 } | |
| 436 name += " @ " + revision | |
| 437 } | |
| 438 build.Summary.Recipe = &resp.Link{ | |
| 439 Label: name, | |
| 440 URL: repoURL, | |
| 441 } | |
| 442 } | |
| 443 } | |
| 444 | |
| 418 func addTaskToBuild(c context.Context, server string, sr *swarming.SwarmingRpcsT askResult, build *resp.MiloBuild) error { | 445 func addTaskToBuild(c context.Context, server string, sr *swarming.SwarmingRpcsT askResult, build *resp.MiloBuild) error { |
| 419 build.Summary.Label = sr.TaskId | 446 build.Summary.Label = sr.TaskId |
| 420 build.Summary.Type = resp.Recipe | 447 build.Summary.Type = resp.Recipe |
| 421 build.Summary.Source = &resp.Link{ | 448 build.Summary.Source = &resp.Link{ |
| 422 Label: "Task " + sr.TaskId, | 449 Label: "Task " + sr.TaskId, |
| 423 URL: taskPageURL(server, sr.TaskId), | 450 URL: taskPageURL(server, sr.TaskId), |
| 424 } | 451 } |
| 425 | 452 |
| 426 // Extract more swarming specific information into the properties. | 453 // Extract more swarming specific information into the properties. |
| 427 if props := taskProperties(sr); len(props.Property) > 0 { | 454 if props := taskProperties(sr); len(props.Property) > 0 { |
| 428 build.PropertyGroup = append(build.PropertyGroup, props) | 455 build.PropertyGroup = append(build.PropertyGroup, props) |
| 429 } | 456 } |
| 430 » if props := tagsToProperties(sr.Tags); len(props.Property) > 0 { | 457 » tags := tagsToMap(sr.Tags) |
| 431 » » build.PropertyGroup = append(build.PropertyGroup, props) | |
| 432 » } | |
| 433 | 458 |
| 434 » addBuilderLink(c, build, server, sr) | 459 » addBuildsetInfo(build, tags) |
| 435 » addBanner(build, sr) | 460 » addBanner(build, tags) |
| 461 » addBuilderLink(c, build, tags) | |
| 462 » addRecipeLink(build, tags) | |
| 436 | 463 |
| 437 // Add a link to the bot. | 464 // Add a link to the bot. |
| 438 if sr.BotId != "" { | 465 if sr.BotId != "" { |
| 439 build.Summary.Bot = &resp.Link{ | 466 build.Summary.Bot = &resp.Link{ |
| 440 Label: sr.BotId, | 467 Label: sr.BotId, |
| 441 URL: botPageURL(server, sr.BotId), | 468 URL: botPageURL(server, sr.BotId), |
| 442 } | 469 } |
| 443 } | 470 } |
| 444 | 471 |
| 445 return nil | 472 return nil |
| (...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 707 for _, tag := range v { | 734 for _, tag := range v { |
| 708 var value string | 735 var value string |
| 709 parts := strings.SplitN(tag, ":", 2) | 736 parts := strings.SplitN(tag, ":", 2) |
| 710 if len(parts) == 2 { | 737 if len(parts) == 2 { |
| 711 value = parts[1] | 738 value = parts[1] |
| 712 } | 739 } |
| 713 res[parts[0]] = value | 740 res[parts[0]] = value |
| 714 } | 741 } |
| 715 return res | 742 return res |
| 716 } | 743 } |
| OLD | NEW |