Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(509)

Unified Diff: milo/appengine/job_source/swarming/build.go

Issue 2949783002: [milo] appengine/* -> * (Closed)
Patch Set: rebase Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « milo/appengine/job_source/swarming/README.md ('k') | milo/appengine/job_source/swarming/buildLog.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: milo/appengine/job_source/swarming/build.go
diff --git a/milo/appengine/job_source/swarming/build.go b/milo/appengine/job_source/swarming/build.go
deleted file mode 100644
index d4e7c0c05881d6ec0e0a91985ec77135e2c4c246..0000000000000000000000000000000000000000
--- a/milo/appengine/job_source/swarming/build.go
+++ /dev/null
@@ -1,777 +0,0 @@
-// Copyright 2015 The LUCI Authors. All rights reserved.
-// Use of this source code is governed under the Apache License, Version 2.0
-// that can be found in the LICENSE file.
-
-package swarming
-
-import (
- "bytes"
- "fmt"
- "net/http"
- "net/url"
- "strings"
- "time"
-
- "golang.org/x/net/context"
-
- swarming "github.com/luci/luci-go/common/api/swarming/swarming/v1"
- "github.com/luci/luci-go/common/errors"
- "github.com/luci/luci-go/common/logging"
- "github.com/luci/luci-go/common/proto/google"
- miloProto "github.com/luci/luci-go/common/proto/milo"
- "github.com/luci/luci-go/common/sync/parallel"
- "github.com/luci/luci-go/logdog/client/annotee"
- "github.com/luci/luci-go/logdog/client/coordinator"
- "github.com/luci/luci-go/logdog/common/types"
- "github.com/luci/luci-go/milo/api/resp"
- "github.com/luci/luci-go/milo/appengine/common"
- "github.com/luci/luci-go/milo/appengine/common/model"
- "github.com/luci/luci-go/milo/appengine/job_source/raw_presentation"
- "github.com/luci/luci-go/server/auth"
-)
-
-// errNotMiloJob is returned if a Swarming task is fetched that does not self-
-// identify as a Milo job.
-var errNotMiloJob = errors.New("Not a Milo Job or access denied")
-
-// SwarmingTimeLayout is time layout used by swarming.
-const SwarmingTimeLayout = "2006-01-02T15:04:05.999999999"
-
-// logDogFetchTimeout is the amount of time to wait while fetching a LogDog
-// stream before we time out the fetch.
-const logDogFetchTimeout = 30 * time.Second
-
-// Swarming task states..
-const (
- // TaskRunning means task is running.
- TaskRunning = "RUNNING"
- // TaskPending means task didn't start yet.
- TaskPending = "PENDING"
- // TaskExpired means task expired and did not start.
- TaskExpired = "EXPIRED"
- // TaskTimedOut means task started, but took too long.
- TaskTimedOut = "TIMED_OUT"
- // TaskBotDied means task started but bot died.
- TaskBotDied = "BOT_DIED"
- // TaskCanceled means the task was canceled. See CompletedTs to determine whether it was started.
- TaskCanceled = "CANCELED"
- // TaskCompleted means task is complete.
- TaskCompleted = "COMPLETED"
-)
-
-func getSwarmingClient(c context.Context, host string) (*swarming.Service, error) {
- c, _ = context.WithTimeout(c, 60*time.Second)
- t, err := auth.GetRPCTransport(c, auth.AsSelf)
- if err != nil {
- return nil, err
- }
- sc, err := swarming.New(&http.Client{Transport: t})
- if err != nil {
- return nil, err
- }
- sc.BasePath = fmt.Sprintf("https://%s/_ah/api/swarming/v1/", host)
- return sc, nil
-}
-
-// swarmingService is an interface that fetches data from Swarming.
-//
-// In production, this is fetched from a Swarming server. For testing, this can
-// be replaced with a mock.
-type swarmingService interface {
- getHost() string
- getSwarmingResult(c context.Context, taskID string) (*swarming.SwarmingRpcsTaskResult, error)
- getSwarmingRequest(c context.Context, taskID string) (*swarming.SwarmingRpcsTaskRequest, error)
- getTaskOutput(c context.Context, taskID string) (string, error)
-}
-
-type prodSwarmingService struct {
- host string
- client *swarming.Service
-}
-
-func newProdService(c context.Context, host string) (*prodSwarmingService, error) {
- client, err := getSwarmingClient(c, host)
- if err != nil {
- return nil, err
- }
- return &prodSwarmingService{
- host: host,
- client: client,
- }, nil
-}
-
-func (svc *prodSwarmingService) getHost() string { return svc.host }
-
-func (svc *prodSwarmingService) getSwarmingResult(c context.Context, taskID string) (*swarming.SwarmingRpcsTaskResult, error) {
- return svc.client.Task.Result(taskID).Context(c).Do()
-}
-
-func (svc *prodSwarmingService) getTaskOutput(c context.Context, taskID string) (string, error) {
- stdout, err := svc.client.Task.Stdout(taskID).Context(c).Do()
- if err != nil {
- return "", err
- }
- return stdout.Output, nil
-}
-
-func (svc *prodSwarmingService) getSwarmingRequest(c context.Context, taskID string) (*swarming.SwarmingRpcsTaskRequest, error) {
- return svc.client.Task.Request(taskID).Context(c).Do()
-}
-
-type swarmingFetchParams struct {
- fetchReq bool
- fetchRes bool
- fetchLog bool
-
- // taskResCallback, if not nil, is a callback that will be invoked after
- // fetching the result, if fetchRes is true. It will be passed a key/value map
- // of the Swarming result's tags.
- //
- // If taskResCallback returns true, any pending log fetch will be cancelled
- // without error.
- taskResCallback func(*swarming.SwarmingRpcsTaskResult) bool
-}
-
-type swarmingFetchResult struct {
- req *swarming.SwarmingRpcsTaskRequest
- res *swarming.SwarmingRpcsTaskResult
-
- // log is the log data content. If no log data was fetched, this will empty.
- // If the log fetch was cancelled, this is undefined.
- log string
-}
-
-// swarmingFetch fetches (in parallel) the components that it is configured to
-// fetch.
-//
-// After fetching, an ACL check is performed to confirm that the user is
-// permitted to view the resulting data. If this check fails, get returns
-// errNotMiloJob.
-func swarmingFetch(c context.Context, svc swarmingService, taskID string, req swarmingFetchParams) (
- *swarmingFetchResult, error) {
-
- // logErr is managed separately from other fetch errors, since in some
- // situations it's acceptable to not have a log stream.
- var logErr error
- var fr swarmingFetchResult
-
- // Special Context to enable the cancellation of log fetching.
- logsCancelled := false
- logCtx, cancelLogs := context.WithCancel(c)
- defer cancelLogs()
-
- err := parallel.FanOutIn(func(workC chan<- func() error) {
- if req.fetchReq {
- workC <- func() (err error) {
- fr.req, err = svc.getSwarmingRequest(c, taskID)
- return
- }
- }
-
- if req.fetchRes {
- workC <- func() (err error) {
- if fr.res, err = svc.getSwarmingResult(c, taskID); err == nil {
- if req.taskResCallback != nil && req.taskResCallback(fr.res) {
- logsCancelled = true
- cancelLogs()
- }
- }
- return
- }
- }
-
- if req.fetchLog {
- workC <- func() error {
- // Note: we're using the log Context here so we can cancel log fetch
- // explicitly.
- fr.log, logErr = svc.getTaskOutput(logCtx, taskID)
- return nil
- }
- }
- })
- if err != nil {
- return nil, err
- }
-
- // Current ACL implementation:
- // If allow_milo:1 is present, it is a public job. Don't bother with ACL check.
- // If it is not present, check the luci_project tag, and see if user is allowed
- // to access said project.
- switch {
- case req.fetchReq:
- if !isAllowed(c, fr.req.Tags) {
- return nil, errNotMiloJob
- }
-
- case req.fetchRes:
- if !isAllowed(c, fr.res.Tags) {
- return nil, errNotMiloJob
- }
-
- default:
- // No metadata to decide if this is a Milo job, so assume that it is not.
- return nil, errNotMiloJob
- }
-
- if req.fetchRes && logErr != nil {
- switch fr.res.State {
- case TaskCompleted, TaskRunning, TaskCanceled:
- default:
- // Ignore log errors if the task might be pending, timed out, expired, etc.
- if err != nil {
- fr.log = ""
- logErr = nil
- }
- }
- }
-
- // If we explicitly cancelled logs, everything is OK.
- if logErr == context.Canceled && logsCancelled {
- logErr = nil
- }
- return &fr, logErr
-}
-
-func taskProperties(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup {
- props := &resp.PropertyGroup{GroupName: "Swarming"}
- if len(sr.CostsUsd) == 1 {
- props.Property = append(props.Property, &resp.Property{
- Key: "Cost of job (USD)",
- Value: fmt.Sprintf("$%.2f", sr.CostsUsd[0]),
- })
- }
- if sr.State == TaskCompleted || sr.State == TaskTimedOut {
- props.Property = append(props.Property, &resp.Property{
- Key: "Exit Code",
- Value: fmt.Sprintf("%d", sr.ExitCode),
- })
- }
- return props
-}
-
-func tagsToMap(tags []string) map[string]string {
- result := make(map[string]string, len(tags))
- for _, t := range tags {
- parts := strings.SplitN(t, ":", 2)
- if len(parts) == 2 {
- result[parts[0]] = parts[1]
- }
- }
- return result
-}
-
-// addBuilderLink adds a link to the buildbucket builder view.
-func addBuilderLink(c context.Context, build *resp.MiloBuild, tags map[string]string) {
- bucket := tags["buildbucket_bucket"]
- builder := tags["builder"]
- if bucket != "" && builder != "" {
- build.Summary.ParentLabel = resp.NewLink(
- builder, fmt.Sprintf("/buildbucket/%s/%s", bucket, builder))
- }
-}
-
-// addBanner adds an OS banner derived from "os" swarming tag, if present.
-func addBanner(build *resp.MiloBuild, tags map[string]string) {
- os := tags["os"]
- var ver string
- parts := strings.SplitN(os, "-", 2)
- if len(parts) == 2 {
- os = parts[0]
- ver = parts[1]
- }
-
- var base resp.LogoBase
- switch os {
- case "Ubuntu":
- base = resp.Ubuntu
- case "Windows":
- base = resp.Windows
- case "Mac":
- base = resp.OSX
- case "Android":
- base = resp.Android
- default:
- return
- }
-
- build.Summary.Banner = &resp.LogoBanner{
- OS: []resp.Logo{{
- LogoBase: base,
- Subtitle: ver,
- Count: 1,
- }},
- }
-}
-
-// addTaskToMiloStep augments a Milo Annotation Protobuf with state from the
-// Swarming task.
-func addTaskToMiloStep(c context.Context, server string, sr *swarming.SwarmingRpcsTaskResult, step *miloProto.Step) error {
- step.Link = &miloProto.Link{
- Label: "Task " + sr.TaskId,
- Value: &miloProto.Link_Url{
- Url: taskPageURL(server, sr.TaskId),
- },
- }
-
- switch sr.State {
- case TaskRunning:
- step.Status = miloProto.Status_RUNNING
-
- case TaskPending:
- step.Status = miloProto.Status_PENDING
-
- case TaskExpired, TaskTimedOut, TaskBotDied:
- step.Status = miloProto.Status_FAILURE
-
- switch sr.State {
- case TaskExpired:
- step.FailureDetails = &miloProto.FailureDetails{
- Type: miloProto.FailureDetails_EXPIRED,
- Text: "Task expired",
- }
- case TaskTimedOut:
- step.FailureDetails = &miloProto.FailureDetails{
- Type: miloProto.FailureDetails_INFRA,
- Text: "Task timed out",
- }
- case TaskBotDied:
- step.FailureDetails = &miloProto.FailureDetails{
- Type: miloProto.FailureDetails_INFRA,
- Text: "Bot died",
- }
- }
-
- case TaskCanceled:
- // Cancelled build is user action, so it is not an infra failure.
- step.Status = miloProto.Status_FAILURE
- step.FailureDetails = &miloProto.FailureDetails{
- Type: miloProto.FailureDetails_CANCELLED,
- Text: "Task cancelled by user",
- }
-
- case TaskCompleted:
-
- switch {
- case sr.InternalFailure:
- step.Status = miloProto.Status_FAILURE
- step.FailureDetails = &miloProto.FailureDetails{
- Type: miloProto.FailureDetails_INFRA,
- }
-
- case sr.Failure:
- step.Status = miloProto.Status_FAILURE
-
- default:
- step.Status = miloProto.Status_SUCCESS
- }
-
- default:
- return fmt.Errorf("unknown swarming task state %q", sr.State)
- }
-
- // Compute start and finished times.
- if sr.StartedTs != "" {
- ts, err := time.Parse(SwarmingTimeLayout, sr.StartedTs)
- if err != nil {
- return fmt.Errorf("invalid task StartedTs: %s", err)
- }
- step.Started = google.NewTimestamp(ts)
- }
- if sr.CompletedTs != "" {
- ts, err := time.Parse(SwarmingTimeLayout, sr.CompletedTs)
- if err != nil {
- return fmt.Errorf("invalid task CompletedTs: %s", err)
- }
- step.Ended = google.NewTimestamp(ts)
- }
-
- return nil
-}
-
-func addBuildsetInfo(build *resp.MiloBuild, tags map[string]string) {
- buildset := tags["buildset"]
- if !strings.HasPrefix(buildset, "patch/") {
- // Buildset isn't a patch, ignore.
- return
- }
-
- patchset := strings.TrimLeft(buildset, "patch/")
- // TODO(hinoka): Also support Rietveld patches.
- if strings.HasPrefix(patchset, "gerrit/") {
- gerritPatchset := strings.TrimLeft(patchset, "gerrit/")
- parts := strings.Split(gerritPatchset, "/")
- if len(parts) != 3 {
- // Not a well-formed gerrit patchset.
- return
- }
- if build.SourceStamp == nil {
- build.SourceStamp = &resp.SourceStamp{}
- }
- build.SourceStamp.Changelist = resp.NewLink(
- "Gerrit CL", fmt.Sprintf("https://%s/c/%s/%s", parts[0], parts[1], parts[2]))
-
- }
-}
-
-func addRecipeLink(build *resp.MiloBuild, tags map[string]string) {
- name := tags["recipe_name"]
- repoURL := tags["recipe_repository"]
- revision := tags["recipe_revision"]
- if name != "" && repoURL != "" {
- if revision == "" {
- revision = "master"
- }
- // Link directly to the revision if it is a gerrit URL, otherwise just
- // display it in the name.
- if repoParse, err := url.Parse(repoURL); err == nil && strings.HasSuffix(
- repoParse.Host, ".googlesource.com") {
- repoURL += "/+/" + revision + "/"
- } else {
- if len(revision) > 8 {
- revision = revision[:8]
- }
- name += " @ " + revision
- }
- build.Summary.Recipe = resp.NewLink(name, repoURL)
- }
-}
-
-func addTaskToBuild(c context.Context, server string, sr *swarming.SwarmingRpcsTaskResult, build *resp.MiloBuild) error {
- build.Summary.Label = sr.TaskId
- build.Summary.Type = resp.Recipe
- build.Summary.Source = resp.NewLink("Task "+sr.TaskId, taskPageURL(server, sr.TaskId))
-
- // Extract more swarming specific information into the properties.
- if props := taskProperties(sr); len(props.Property) > 0 {
- build.PropertyGroup = append(build.PropertyGroup, props)
- }
- tags := tagsToMap(sr.Tags)
-
- addBuildsetInfo(build, tags)
- addBanner(build, tags)
- addBuilderLink(c, build, tags)
- addRecipeLink(build, tags)
-
- // Add a link to the bot.
- if sr.BotId != "" {
- build.Summary.Bot = resp.NewLink(sr.BotId, botPageURL(server, sr.BotId))
- }
-
- return nil
-}
-
-// streamsFromAnnotatedLog takes in an annotated log and returns a fully
-// populated set of logdog streams
-func streamsFromAnnotatedLog(ctx context.Context, log string) (*raw_presentation.Streams, error) {
- c := &memoryClient{}
- p := annotee.New(ctx, annotee.Options{
- Client: c,
- MetadataUpdateInterval: -1, // Neverrrrrr send incr updates.
- Offline: true,
- })
-
- is := annotee.Stream{
- Reader: bytes.NewBufferString(log),
- Name: types.StreamName("stdout"),
- Annotate: true,
- StripAnnotations: true,
- }
- // If this ever has more than one stream then memoryClient needs to become
- // goroutine safe
- if err := p.RunStreams([]*annotee.Stream{&is}); err != nil {
- return nil, err
- }
- p.Finish()
- return c.ToLogDogStreams()
-}
-
-// buildLoader represents the ability to load a Milo build from a Swarming task.
-//
-// It exists so that the internal build loading functionality can be stubbed out
-// for testing.
-type buildLoader struct {
- // logdogClientFunc returns a coordinator Client instance for the supplied
- // parameters.
- //
- // If nil, a production client will be generated.
- logDogClientFunc func(c context.Context, host string) (*coordinator.Client, error)
-}
-
-func (bl *buildLoader) newEmptyAnnotationStream(c context.Context, addr *types.StreamAddr) (
- *raw_presentation.AnnotationStream, error) {
-
- fn := bl.logDogClientFunc
- if fn == nil {
- fn = raw_presentation.NewClient
- }
- client, err := fn(c, addr.Host)
- if err != nil {
- return nil, errors.Annotate(err).Reason("failed to create LogDog client").Err()
- }
-
- as := raw_presentation.AnnotationStream{
- Client: client,
- Project: addr.Project,
- Path: addr.Path,
- }
- if err := as.Normalize(); err != nil {
- return nil, errors.Annotate(err).Reason("failed to normalize annotation stream parameters").Err()
- }
-
- return &as, nil
-}
-
-// failedToStart is called in the case where logdog-only mode is on but the
-// stream doesn't exist and the swarming job is complete. It modifies the build
-// to add information that would've otherwise been in the annotation stream.
-func failedToStart(c context.Context, build *resp.MiloBuild, res *swarming.SwarmingRpcsTaskResult, host string) error {
- var err error
- build.Summary.Status = model.InfraFailure
- build.Summary.Started, err = time.Parse(SwarmingTimeLayout, res.StartedTs)
- if err != nil {
- return err
- }
- build.Summary.Finished, err = time.Parse(SwarmingTimeLayout, res.CompletedTs)
- if err != nil {
- return err
- }
- build.Summary.Duration = build.Summary.Finished.Sub(build.Summary.Started)
- infoComp := infoComponent(model.InfraFailure,
- "LogDog stream not found", "Job likely failed to start.")
- infoComp.Started = build.Summary.Started
- infoComp.Finished = build.Summary.Finished
- infoComp.Duration = build.Summary.Duration
- infoComp.Verbosity = resp.Interesting
- build.Components = append(build.Components, infoComp)
- return addTaskToBuild(c, host, res, build)
-}
-
-func (bl *buildLoader) swarmingBuildImpl(c context.Context, svc swarmingService, linkBase, taskID string) (*resp.MiloBuild, error) {
- // Fetch the data from Swarming
- var logDogStreamAddr *types.StreamAddr
-
- fetchParams := swarmingFetchParams{
- fetchRes: true,
- fetchLog: true,
-
- // Cancel if LogDog annotation stream parameters are present in the tag set.
- taskResCallback: func(res *swarming.SwarmingRpcsTaskResult) (cancelLogs bool) {
- // If the build hasn't started yet, then there is no LogDog log stream to
- // render.
- switch res.State {
- case TaskPending, TaskExpired:
- return false
-
- case TaskCanceled:
- // If the task wasn't created, then it wasn't started.
- if res.CreatedTs == "" {
- return false
- }
- }
-
- // The task started ... is it using LogDog for logging?
- tags := swarmingTags(res.Tags)
-
- var err error
- if logDogStreamAddr, err = resolveLogDogStreamAddrFromTags(tags, res.TaskId, res.TryNumber); err != nil {
- logging.WithError(err).Debugf(c, "Not using LogDog annotation stream.")
- return false
- }
- return true
- },
- }
- fr, err := swarmingFetch(c, svc, taskID, fetchParams)
- if err != nil {
- return nil, err
- }
-
- var build resp.MiloBuild
- var s *miloProto.Step
- var lds *raw_presentation.Streams
- var ub raw_presentation.URLBuilder
-
- // Load the build from the available data.
- //
- // If the Swarming task explicitly specifies its log location, we prefer that.
- // As a fallback, we will try and parse the Swarming task's output for
- // annotations.
- switch {
- case logDogStreamAddr != nil:
- logging.Infof(c, "Loading build from LogDog stream at: %s", logDogStreamAddr)
-
- // If the LogDog stream is available, load the step from that.
- as, err := bl.newEmptyAnnotationStream(c, logDogStreamAddr)
- if err != nil {
- return nil, errors.Annotate(err).Reason("failed to create LogDog annotation stream").Err()
- }
-
- prefix, _ := logDogStreamAddr.Path.Split()
- ub = &raw_presentation.ViewerURLBuilder{
- Host: logDogStreamAddr.Host,
- Prefix: prefix,
- Project: logDogStreamAddr.Project,
- }
-
- if s, err = as.Fetch(c); err != nil {
- switch errors.Unwrap(err) {
- case coordinator.ErrNoSuchStream:
- // The stream was not found. This could be due to one of two things:
- // 1. The step just started and we're just waiting for the logs
- // to propogage to logdog.
- // 2. The bootsrap on the client failed, and never sent data to logdog.
- // This would be evident because the swarming result would be a failure.
- if fr.res.State == TaskCompleted {
- err = failedToStart(c, &build, fr.res, svc.getHost())
- return &build, err
- }
- logging.WithError(err).Errorf(c, "User cannot access stream.")
- build.Components = append(build.Components, infoComponent(model.Running,
- "Waiting...", "waiting for annotation stream"))
-
- case coordinator.ErrNoAccess:
- logging.WithError(err).Errorf(c, "User cannot access stream.")
- build.Components = append(build.Components, infoComponent(model.Failure,
- "No Access", "no access to annotation stream"))
-
- default:
- logging.WithError(err).Errorf(c, "Failed to load LogDog annotation stream.")
- build.Components = append(build.Components, infoComponent(model.InfraFailure,
- "Error", "failed to load annotation stream"))
- }
- }
-
- case fr.log != "":
- // Decode the data using annotee. The logdog stream returned here is assumed
- // to be consistent, which is why the following block of code are not
- // expected to ever err out.
- var err error
- lds, err = streamsFromAnnotatedLog(c, fr.log)
- if err != nil {
- comp := infoComponent(model.InfraFailure, "Milo annotation parser", err.Error())
- comp.SubLink = append(comp.SubLink, resp.LinkSet{
- resp.NewLink("swarming task", taskPageURL(svc.getHost(), taskID)),
- })
- build.Components = append(build.Components, comp)
- }
-
- if lds != nil && lds.MainStream != nil && lds.MainStream.Data != nil {
- s = lds.MainStream.Data
- }
- ub = swarmingURLBuilder(linkBase)
-
- default:
- s = &miloProto.Step{}
- ub = swarmingURLBuilder(linkBase)
- }
-
- if s != nil {
- if err := addTaskToMiloStep(c, svc.getHost(), fr.res, s); err != nil {
- return nil, err
- }
- raw_presentation.AddLogDogToBuild(c, ub, s, &build)
- }
-
- if err := addTaskToBuild(c, svc.getHost(), fr.res, &build); err != nil {
- return nil, err
- }
-
- return &build, nil
-}
-
-func infoComponent(st model.Status, label, text string) *resp.BuildComponent {
- return &resp.BuildComponent{
- Type: resp.Summary,
- Label: label,
- Text: []string{text},
- Status: st,
- }
-}
-
-// isAllowed checks if:
-// 1. allow_milo:1 is present. If so, it's a public job.
-// 2. luci_project is present, and if the logged in user has access to that project.
-func isAllowed(c context.Context, tags []string) bool {
- for _, t := range tags {
- if t == "allow_milo:1" {
- return true
- }
- }
- for _, t := range tags {
- if strings.HasPrefix(t, "luci_project:") {
- sp := strings.SplitN(t, ":", 2)
- if len(sp) != 2 {
- return false
- }
- logging.Debugf(c, "Checking if user has access to %s", sp[1])
- // sp[1] is the project ID.
- allowed, err := common.IsAllowed(c, sp[1])
- if err != nil {
- logging.WithError(err).Errorf(c, "could not perform acl check")
- return false
- }
- return allowed
- }
- }
- return false
-}
-
-// taskPageURL returns a URL to a human-consumable page of a swarming task.
-// Supports server aliases.
-func taskPageURL(swarmingHostname, taskID string) string {
- return fmt.Sprintf("https://%s/task?id=%s&show_raw=1&wide_logs=true", swarmingHostname, taskID)
-}
-
-// botPageURL returns a URL to a human-consumable page of a swarming bot.
-// Supports server aliases.
-func botPageURL(swarmingHostname, botID string) string {
- return fmt.Sprintf("https://%s/restricted/bot/%s", swarmingHostname, botID)
-}
-
-// swarmingURLBuilder is a logdog.URLBuilder that builds Milo swarming log
-// links.
-//
-// The string value for this should be the "linkBase" parameter value supplied
-// to swarmingBuildImpl.
-type swarmingURLBuilder string
-
-func (b swarmingURLBuilder) BuildLink(l *miloProto.Link) *resp.Link {
- u, err := url.Parse(string(b))
- if err != nil {
- return nil
- }
-
- switch t := l.Value.(type) {
- case *miloProto.Link_LogdogStream:
- ls := t.LogdogStream
-
- if u.Path == "" {
- u.Path = ls.Name
- } else {
- u.Path = strings.TrimSuffix(u.Path, "/") + "/" + ls.Name
- }
- link := resp.NewLink(l.Label, u.String())
- if link.Label == "" {
- link.Label = ls.Name
- }
- return link
-
- case *miloProto.Link_Url:
- return resp.NewLink(l.Label, t.Url)
-
- default:
- return nil
- }
-}
-
-func swarmingTags(v []string) map[string]string {
- res := make(map[string]string, len(v))
- for _, tag := range v {
- var value string
- parts := strings.SplitN(tag, ":", 2)
- if len(parts) == 2 {
- value = parts[1]
- }
- res[parts[0]] = value
- }
- return res
-}
« no previous file with comments | « milo/appengine/job_source/swarming/README.md ('k') | milo/appengine/job_source/swarming/buildLog.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698