blob: 9b22251650715afb0b637e7f1ee70217cb04ee8c [file] [log] [blame]
// Copyright 2024 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 host
import (
"context"
"fmt"
"os"
"path/filepath"
bbpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/lucictx"
)
// Find CAS Client. It should have been downloaded when bbagent installs
// CIPD packages.
func findCasClient(workDir string, b *bbpb.Build) (string, error) {
var bbagentUtilityPath string
for path, purpose := range b.Infra.Buildbucket.Agent.Purposes {
if purpose == bbpb.BuildInfra_Buildbucket_Agent_PURPOSE_BBAGENT_UTILITY {
bbagentUtilityPath = path
break
}
}
if bbagentUtilityPath == "" {
return "", errors.Reason("Failed to find bbagent utility packages").Err()
}
casClient, err := processCmd(filepath.Join(workDir, bbagentUtilityPath), "cas")
if err != nil {
return "", err
}
return casClient, nil
}
func generateCasCmd(casClient, outputDir string, casRef *bbpb.InputDataRef_CAS) []string {
return []string{
casClient,
"download",
"-cas-instance",
casRef.CasInstance,
"-digest",
fmt.Sprintf("%s/%d", casRef.Digest.Hash, casRef.Digest.SizeBytes),
"-dir",
outputDir,
"-log-level",
"info",
}
}
func execCasCmd(ctx context.Context, args []string) error {
// Switch to swarming system account to download CAS inputs, it won't
// work for non-swarming backends.
sysCtx, err := lucictx.SwitchLocalAccount(ctx, "system")
if errors.Is(err, lucictx.ErrNoLocalAuthAccount) {
logging.Infof(ctx, "Failed to find 'system' account; maybe in non-swarming environment?")
// Continue to execute cas.
sysCtx = ctx
} else if err != nil {
return errors.Annotate(err, "could not switch to 'system' account in LUCI_CONTEXT").Err()
}
cmd := execCommandContext(sysCtx, args[0], args[1:]...)
logging.Infof(ctx, "Running command: %s", cmd.String())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return errors.Annotate(cmd.Run(), "Failed to run cas download").Err()
}
func downloadCasFiles(ctx context.Context, b *bbpb.Build, workDir string) error {
var casClient string
var err error
inputData := b.GetInfra().GetBuildbucket().GetAgent().GetInput().GetData()
for dir, ref := range inputData {
casRef := ref.GetCas()
if casRef == nil {
continue
}
if casClient == "" {
if casClient, err = findCasClient(workDir, b); err != nil {
return errors.Annotate(err, "download cas files").Err()
}
}
outDir := filepath.Join(workDir, dir)
cmdArgs := generateCasCmd(casClient, outDir, casRef)
if err = execCasCmd(ctx, cmdArgs); err != nil {
return errors.Annotate(err, "download cas files").Err()
}
}
// TODO(chanli): populate ResolvedDataRef_CAS as more things use CAS in
// buildbucket.
return nil
}