blob: a32b80fabf6747b0191dafa9a9f511de52f9d5f5 [file] [log] [blame]
// Copyright 2018 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package policy
import (
"fmt"
"strings"
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/buildbucket/protoutil"
bbv1 "go.chromium.org/luci/common/api/buildbucket/buildbucket/v1"
"go.chromium.org/luci/common/api/gitiles"
"go.chromium.org/luci/common/data/stringset"
"go.chromium.org/luci/common/data/strpair"
"go.chromium.org/luci/scheduler/api/scheduler/v1"
"go.chromium.org/luci/scheduler/appengine/internal"
"go.chromium.org/luci/scheduler/appengine/task"
)
// RequestBuilder is a task.Request in a process of being prepared.
type RequestBuilder struct {
task.Request
env Environment // for logging
}
// DebugLog adds a line to the request log and the triage log.
func (r *RequestBuilder) DebugLog(format string, args ...interface{}) {
r.Request.DebugLog += fmt.Sprintf(format+"\n", args...)
if r.env != nil {
r.env.DebugLog(format, args...)
}
}
// FromTrigger derives the request properties from the given trigger.
func (r *RequestBuilder) FromTrigger(t *internal.Trigger) {
switch p := t.Payload.(type) {
case *internal.Trigger_Cron:
r.FromCronTrigger(p.Cron)
case *internal.Trigger_Webui:
r.FromWebUITrigger(p.Webui)
case *internal.Trigger_Noop:
r.FromNoopTrigger(p.Noop)
case *internal.Trigger_Gitiles:
r.FromGitilesTrigger(p.Gitiles)
case *internal.Trigger_Buildbucket:
r.FromBuildbucketTrigger(p.Buildbucket)
default:
r.DebugLog("Unrecognized trigger payload of type %T, ignoring", p)
}
}
// FromCronTrigger derives the request properties from the given cron trigger.
func (r *RequestBuilder) FromCronTrigger(t *scheduler.CronTrigger) {
// nothing here for now
}
// FromWebUITrigger derives the request properties from the given web UI
// trigger.
func (r *RequestBuilder) FromWebUITrigger(t *scheduler.WebUITrigger) {
// nothing here for now
}
// FromNoopTrigger derives the request properties from the given noop trigger.
func (r *RequestBuilder) FromNoopTrigger(t *scheduler.NoopTrigger) {
r.Properties = mergeIntoStruct(&structpb.Struct{}, map[string]string{
"noop_trigger_data": t.Data, // for testing
})
}
// FromGitilesTrigger derives the request properties from the given gitiles
// trigger.
//
// TODO(crbug.com/1182002): Remove properties/tags modifications here once there
// are no in-flight triggers that might hit Buildbucket v1 code path.
func (r *RequestBuilder) FromGitilesTrigger(t *scheduler.GitilesTrigger) {
repo, err := gitiles.NormalizeRepoURL(t.Repo, false)
if err != nil {
r.DebugLog("Bad repo URL %q in the trigger - %s", t.Repo, err)
return
}
// Merge properties derived from the commit info on top t.Properties.
if t.Properties != nil && len(t.Properties.Fields) != 0 {
r.Properties = proto.Clone(t.Properties).(*structpb.Struct)
} else {
r.Properties = &structpb.Struct{
Fields: make(map[string]*structpb.Value, 3),
}
}
mergeIntoStruct(r.Properties, map[string]string{
"revision": t.Revision,
"branch": t.Ref,
"repository": t.Repo,
})
commit := &buildbucketpb.GitilesCommit{
Host: repo.Host,
Project: strings.TrimPrefix(repo.Path, "/"),
Id: t.Revision,
}
// Join t.Tags with tags derived from the commit.
r.Tags = make([]string, 0, len(t.Tags)+3)
r.Tags = append(
r.Tags,
strpair.Format(bbv1.TagBuildSet, protoutil.GitilesBuildSet(commit)),
strpair.Format("gitiles_ref", t.Ref),
)
r.Tags = append(r.Tags, t.Tags...)
r.Tags = removeDups(r.Tags)
}
// FromBuildbucketTrigger derives the request properties from the given
// buildbucket trigger.
func (r *RequestBuilder) FromBuildbucketTrigger(t *scheduler.BuildbucketTrigger) {
r.Properties = t.Properties
r.Tags = t.Tags
}
////////////////////////////////////////////////////////////////////////////////
// mergeIntoStruct merges `m` into protobuf.Struct returning it.
func mergeIntoStruct(s *structpb.Struct, m map[string]string) *structpb.Struct {
if s.Fields == nil {
s.Fields = make(map[string]*structpb.Value, len(m))
}
for k, v := range m {
s.Fields[k] = &structpb.Value{
Kind: &structpb.Value_StringValue{StringValue: v},
}
}
return s
}
// removeDups removes duplicates from the list, modifying it in-place.
func removeDups(l []string) []string {
seen := stringset.New(len(l))
filtered := l[:0]
for _, s := range l {
if seen.Add(s) {
filtered = append(filtered, s)
}
}
return filtered
}