blob: 7a9e00fbfd34162b573826cb1e90c3eeb871f699 [file] [log] [blame]
// Copyright 2020 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 job
import (
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"go.chromium.org/luci/buildbucket"
bbpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/buildbucket/protoutil"
"go.chromium.org/luci/common/data/stringset"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/luciexe/exe"
swarmingpb "go.chromium.org/luci/swarming/proto/api_v2"
)
// WriteProperties writes an input property on this Buildbucket message.
func (b *Buildbucket) WriteProperties(inputs map[string]any) {
b.EnsureBasics()
if err := exe.WriteProperties(b.BbagentArgs.Build.Input.Properties, inputs); err != nil {
panic(errors.Annotate(err, "impossible").Err())
}
}
// EnsureBasics ensures that the following fields are non-nil:
//
// b.BbagentArgs
// b.BbagentArgs.Build
// b.BbagentArgs.Build.Exe
// b.BbagentArgs.Build.Infra
// b.BbagentArgs.Build.Infra.Buildbucket
// b.BbagentArgs.Build.Infra.Logdog
// b.BbagentArgs.Build.Input
// b.BbagentArgs.Build.Input.Properties
//
// b.BbagentArgs.Build.Infra.Swarming is also ensured in the case that
// b.BbagentArgs.Build.Infra.Backend is nil.
func (b *Buildbucket) EnsureBasics() {
proto.Merge(b, &Buildbucket{BbagentArgs: &bbpb.BBAgentArgs{Build: &bbpb.Build{
Exe: &bbpb.Executable{},
Input: &bbpb.Build_Input{
Properties: &structpb.Struct{},
},
Infra: &bbpb.BuildInfra{
Buildbucket: &bbpb.BuildInfra_Buildbucket{},
Logdog: &bbpb.BuildInfra_LogDog{},
},
}}})
if b.BbagentArgs.Build.Infra.Swarming == nil && b.BbagentArgs.Build.Infra.Backend == nil {
b.BbagentArgs.Build.Infra.Swarming = &bbpb.BuildInfra_Swarming{}
}
}
// UpdateBuildbucketAgent updates or populates b.BbagentArgs.Build.Infra.Buildbucket.Agent.
func (b *Buildbucket) UpdateBuildbucketAgent(updates *bbpb.BuildInfra_Buildbucket_Agent) {
if b.BbagentArgs.Build.Infra.Buildbucket.GetAgent() == nil {
b.BbagentArgs.Build.Infra.Buildbucket.Agent = &bbpb.BuildInfra_Buildbucket_Agent{}
}
proto.Merge(b.BbagentArgs.Build.Infra.Buildbucket.Agent, updates)
}
func (b *Buildbucket) updateBuildbucketAgentPayloadPath(newPath string) {
for p, purpose := range b.BbagentArgs.Build.Infra.Buildbucket.GetAgent().GetPurposes() {
if purpose == bbpb.BuildInfra_Buildbucket_Agent_PURPOSE_EXE_PAYLOAD {
delete(b.BbagentArgs.Build.Infra.Buildbucket.Agent.Purposes, p)
}
}
b.UpdateBuildbucketAgent(&bbpb.BuildInfra_Buildbucket_Agent{
Purposes: map[string]bbpb.BuildInfra_Buildbucket_Agent_Purpose{
newPath: bbpb.BuildInfra_Buildbucket_Agent_PURPOSE_EXE_PAYLOAD,
},
})
}
// UpdateBuildFromBbagentArgs populates fields in b.BbagentArgs.Build
// from b.BbagentArgs.
func (b *Buildbucket) UpdateBuildFromBbagentArgs() {
b.EnsureBasics()
if b.BbagentArgs.Build.Infra.GetBbagent() == nil {
b.BbagentArgs.Build.Infra.Bbagent = &bbpb.BuildInfra_BBAgent{
PayloadPath: b.BbagentArgs.PayloadPath,
CacheDir: b.BbagentArgs.CacheDir,
}
}
b.BbagentArgs.Build.Infra.Buildbucket.KnownPublicGerritHosts = b.BbagentArgs.KnownPublicGerritHosts
b.updateBuildbucketAgentPayloadPath(b.BbagentArgs.PayloadPath)
}
// UpdatePayloadPath updates the payload path of the led build.
func (b *Buildbucket) UpdatePayloadPath(newPath string) {
b.BbagentArgs.PayloadPath = newPath
if b.BbagentArgs.Build.Infra.GetBbagent() == nil {
b.BbagentArgs.Build.Infra.Bbagent = &bbpb.BuildInfra_BBAgent{}
}
b.BbagentArgs.Build.Infra.Bbagent.PayloadPath = newPath
b.updateBuildbucketAgentPayloadPath(newPath)
}
// PayloadPath returns the payload path of the led build.
func (b *Buildbucket) PayloadPath() string {
return protoutil.ExePayloadPath(b.BbagentArgs.GetBuild())
}
func (b *Buildbucket) Payload() (*bbpb.InputDataRef_CAS, *bbpb.InputDataRef_CIPD) {
payloadPath := b.PayloadPath()
inputData := b.BbagentArgs.GetBuild().GetInfra().GetBuildbucket().GetAgent().GetInput().GetData()
if inputData == nil {
return nil, nil
}
if ref, ok := inputData[payloadPath]; ok {
return ref.GetCas(), ref.GetCipd()
}
return nil, nil
}
func (b *Buildbucket) CacheDir() string {
return protoutil.CacheDir(b.BbagentArgs.GetBuild())
}
// BbagentDownloadCIPDPkgs returns a bool on whether bbagent is responsible for
// downloading CIPD packages.
func (b *Buildbucket) BbagentDownloadCIPDPkgs() bool {
return !b.LegacyKitchen && stringset.NewFromSlice(b.GetBbagentArgs().GetBuild().GetInput().GetExperiments()...).Has(buildbucket.ExperimentBBAgentDownloadCipd)
}
func (b *Buildbucket) UpdateLedProperties() error {
curLedProps := &ledProperties{}
err := exe.ParseProperties(b.GetBbagentArgs().GetBuild().GetInput().GetProperties(), map[string]any{
"$recipe_engine/led": curLedProps,
})
if err != nil {
return errors.Annotate(err, `failed to parse input property "$recipe_engine/led"`).Err()
}
props := &ledProperties{
ShadowedBucket: curLedProps.ShadowedBucket,
}
cas, cipd := b.Payload()
switch {
case len(cipd.GetSpecs()) > 0:
props.CIPDInput = &cipdInput{
Package: cipd.Specs[0].GetPackage(),
Version: cipd.Specs[0].GetVersion(),
}
case cas != nil:
props.RbeCasInput = &swarmingpb.CASReference{
CasInstance: cas.GetCasInstance(),
Digest: &swarmingpb.Digest{
Hash: cas.GetDigest().GetHash(),
SizeBytes: cas.GetDigest().GetSizeBytes(),
},
}
}
b.WriteProperties(map[string]any{
"$recipe_engine/led": nil,
})
b.WriteProperties(map[string]any{
"$recipe_engine/led": props,
})
return nil
}