go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/led/job/buildbucket.go (about)

     1  // Copyright 2020 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package job
    16  
    17  import (
    18  	"github.com/golang/protobuf/proto"
    19  	"google.golang.org/protobuf/types/known/structpb"
    20  
    21  	"go.chromium.org/luci/buildbucket"
    22  	bbpb "go.chromium.org/luci/buildbucket/proto"
    23  	"go.chromium.org/luci/buildbucket/protoutil"
    24  	"go.chromium.org/luci/common/data/stringset"
    25  	"go.chromium.org/luci/common/errors"
    26  	"go.chromium.org/luci/luciexe/exe"
    27  	swarmingpb "go.chromium.org/luci/swarming/proto/api_v2"
    28  )
    29  
    30  // WriteProperties writes an input property on this Buildbucket message.
    31  func (b *Buildbucket) WriteProperties(inputs map[string]any) {
    32  	b.EnsureBasics()
    33  
    34  	if err := exe.WriteProperties(b.BbagentArgs.Build.Input.Properties, inputs); err != nil {
    35  		panic(errors.Annotate(err, "impossible").Err())
    36  	}
    37  }
    38  
    39  // EnsureBasics ensures that the following fields are non-nil:
    40  //
    41  //	b.BbagentArgs
    42  //	b.BbagentArgs.Build
    43  //	b.BbagentArgs.Build.Exe
    44  //	b.BbagentArgs.Build.Infra
    45  //	b.BbagentArgs.Build.Infra.Buildbucket
    46  //	b.BbagentArgs.Build.Infra.Logdog
    47  //	b.BbagentArgs.Build.Input
    48  //	b.BbagentArgs.Build.Input.Properties
    49  //
    50  // b.BbagentArgs.Build.Infra.Swarming is also ensured in the case that
    51  // b.BbagentArgs.Build.Infra.Backend is nil.
    52  func (b *Buildbucket) EnsureBasics() {
    53  	proto.Merge(b, &Buildbucket{BbagentArgs: &bbpb.BBAgentArgs{Build: &bbpb.Build{
    54  		Exe: &bbpb.Executable{},
    55  		Input: &bbpb.Build_Input{
    56  			Properties: &structpb.Struct{},
    57  		},
    58  		Infra: &bbpb.BuildInfra{
    59  			Buildbucket: &bbpb.BuildInfra_Buildbucket{},
    60  			Logdog:      &bbpb.BuildInfra_LogDog{},
    61  		},
    62  	}}})
    63  
    64  	if b.BbagentArgs.Build.Infra.Swarming == nil && b.BbagentArgs.Build.Infra.Backend == nil {
    65  		b.BbagentArgs.Build.Infra.Swarming = &bbpb.BuildInfra_Swarming{}
    66  	}
    67  }
    68  
    69  // UpdateBuildbucketAgent updates or populates b.BbagentArgs.Build.Infra.Buildbucket.Agent.
    70  func (b *Buildbucket) UpdateBuildbucketAgent(updates *bbpb.BuildInfra_Buildbucket_Agent) {
    71  	if b.BbagentArgs.Build.Infra.Buildbucket.GetAgent() == nil {
    72  		b.BbagentArgs.Build.Infra.Buildbucket.Agent = &bbpb.BuildInfra_Buildbucket_Agent{}
    73  	}
    74  	proto.Merge(b.BbagentArgs.Build.Infra.Buildbucket.Agent, updates)
    75  }
    76  
    77  func (b *Buildbucket) updateBuildbucketAgentPayloadPath(newPath string) {
    78  	for p, purpose := range b.BbagentArgs.Build.Infra.Buildbucket.GetAgent().GetPurposes() {
    79  		if purpose == bbpb.BuildInfra_Buildbucket_Agent_PURPOSE_EXE_PAYLOAD {
    80  			delete(b.BbagentArgs.Build.Infra.Buildbucket.Agent.Purposes, p)
    81  		}
    82  	}
    83  	b.UpdateBuildbucketAgent(&bbpb.BuildInfra_Buildbucket_Agent{
    84  		Purposes: map[string]bbpb.BuildInfra_Buildbucket_Agent_Purpose{
    85  			newPath: bbpb.BuildInfra_Buildbucket_Agent_PURPOSE_EXE_PAYLOAD,
    86  		},
    87  	})
    88  }
    89  
    90  // UpdateBuildFromBbagentArgs populates fields in b.BbagentArgs.Build
    91  // from b.BbagentArgs.
    92  func (b *Buildbucket) UpdateBuildFromBbagentArgs() {
    93  	b.EnsureBasics()
    94  
    95  	if b.BbagentArgs.Build.Infra.GetBbagent() == nil {
    96  		b.BbagentArgs.Build.Infra.Bbagent = &bbpb.BuildInfra_BBAgent{
    97  			PayloadPath: b.BbagentArgs.PayloadPath,
    98  			CacheDir:    b.BbagentArgs.CacheDir,
    99  		}
   100  	}
   101  
   102  	b.BbagentArgs.Build.Infra.Buildbucket.KnownPublicGerritHosts = b.BbagentArgs.KnownPublicGerritHosts
   103  	b.updateBuildbucketAgentPayloadPath(b.BbagentArgs.PayloadPath)
   104  }
   105  
   106  // UpdatePayloadPath updates the payload path of the led build.
   107  func (b *Buildbucket) UpdatePayloadPath(newPath string) {
   108  	b.BbagentArgs.PayloadPath = newPath
   109  	if b.BbagentArgs.Build.Infra.GetBbagent() == nil {
   110  		b.BbagentArgs.Build.Infra.Bbagent = &bbpb.BuildInfra_BBAgent{}
   111  	}
   112  	b.BbagentArgs.Build.Infra.Bbagent.PayloadPath = newPath
   113  	b.updateBuildbucketAgentPayloadPath(newPath)
   114  }
   115  
   116  // PayloadPath returns the payload path of the led build.
   117  func (b *Buildbucket) PayloadPath() string {
   118  	return protoutil.ExePayloadPath(b.BbagentArgs.GetBuild())
   119  }
   120  
   121  func (b *Buildbucket) Payload() (*bbpb.InputDataRef_CAS, *bbpb.InputDataRef_CIPD) {
   122  	payloadPath := b.PayloadPath()
   123  	inputData := b.BbagentArgs.GetBuild().GetInfra().GetBuildbucket().GetAgent().GetInput().GetData()
   124  	if inputData == nil {
   125  		return nil, nil
   126  	}
   127  	if ref, ok := inputData[payloadPath]; ok {
   128  		return ref.GetCas(), ref.GetCipd()
   129  	}
   130  	return nil, nil
   131  }
   132  
   133  func (b *Buildbucket) CacheDir() string {
   134  	return protoutil.CacheDir(b.BbagentArgs.GetBuild())
   135  }
   136  
   137  // BbagentDownloadCIPDPkgs returns a bool on whether bbagent is responsible for
   138  // downloading CIPD packages.
   139  func (b *Buildbucket) BbagentDownloadCIPDPkgs() bool {
   140  	return !b.LegacyKitchen && stringset.NewFromSlice(b.GetBbagentArgs().GetBuild().GetInput().GetExperiments()...).Has(buildbucket.ExperimentBBAgentDownloadCipd)
   141  }
   142  
   143  func (b *Buildbucket) UpdateLedProperties() error {
   144  	curLedProps := &ledProperties{}
   145  	err := exe.ParseProperties(b.GetBbagentArgs().GetBuild().GetInput().GetProperties(), map[string]any{
   146  		"$recipe_engine/led": curLedProps,
   147  	})
   148  	if err != nil {
   149  		return errors.Annotate(err, `failed to parse input property "$recipe_engine/led"`).Err()
   150  	}
   151  	props := &ledProperties{
   152  		ShadowedBucket: curLedProps.ShadowedBucket,
   153  	}
   154  
   155  	cas, cipd := b.Payload()
   156  	switch {
   157  	case len(cipd.GetSpecs()) > 0:
   158  		props.CIPDInput = &cipdInput{
   159  			Package: cipd.Specs[0].GetPackage(),
   160  			Version: cipd.Specs[0].GetVersion(),
   161  		}
   162  	case cas != nil:
   163  		props.RbeCasInput = &swarmingpb.CASReference{
   164  			CasInstance: cas.GetCasInstance(),
   165  			Digest: &swarmingpb.Digest{
   166  				Hash:      cas.GetDigest().GetHash(),
   167  				SizeBytes: cas.GetDigest().GetSizeBytes(),
   168  			},
   169  		}
   170  	}
   171  
   172  	b.WriteProperties(map[string]any{
   173  		"$recipe_engine/led": nil,
   174  	})
   175  	b.WriteProperties(map[string]any{
   176  		"$recipe_engine/led": props,
   177  	})
   178  	return nil
   179  }