go.fuchsia.dev/infra@v0.0.0-20240507153436-9b593402251b/cmd/buildsetlookup/main.go (about)

     1  // Copyright 2018 The Fuchsia Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package main
     6  
     7  // TODO(nmulcahey): Implement general Search RPC support in the buildbucket
     8  // CLI and remove this + LKGS
     9  
    10  import (
    11  	"context"
    12  	"flag"
    13  	"fmt"
    14  	"log"
    15  	"os"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"go.chromium.org/luci/auth"
    20  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    21  	"go.chromium.org/luci/grpc/prpc"
    22  
    23  	"go.chromium.org/luci/auth/client/authcli"
    24  	"go.chromium.org/luci/hardcoded/chromeinfra"
    25  )
    26  
    27  var (
    28  	host        string
    29  	builderID   string
    30  	buildSet    string
    31  	buildStatus int
    32  	output      string
    33  	// LUCI flags used to parse command-line authentication options.
    34  	authFlags authcli.Flags
    35  )
    36  
    37  func init() {
    38  	flag.StringVar(&host, "host", "cr-buildbucket.appspot.com", "the buildbucket host to use (default is cr-buildbucket.appspot.com)")
    39  	flag.StringVar(&builderID, "builder-id", "", "name of the builders to use as a reference (e.g. fuchsia/ci/garnet-x64)")
    40  	flag.StringVar(&buildSet, "build-set", "", "a unique buildset id in buildbucket (e.g. commit/gitiles/fuchsia.googlesource.com/topaz/+/e3127e0bd6d57da7a5959ee70eb0a396590e6d53)")
    41  	flag.IntVar(&buildStatus, "build-status", int(buildbucketpb.Status_SUCCESS), "expected build status (default is 12, SUCCESS). see https://godoc.org/go.chromium.org/luci/buildbucket/proto#Status")
    42  	flag.StringVar(&output, "output-file", "", "name of the file to write snapshot to (default is stdout)")
    43  
    44  	authFlags = authcli.Flags{}
    45  	authFlags.Register(flag.CommandLine, chromeinfra.DefaultAuthOptions())
    46  }
    47  
    48  // praseBuilderID parses a builder ID of the form "fuchsia/ci/garnet-x64".
    49  func parseBuilderID(builderID string) (*buildbucketpb.BuilderID, error) {
    50  	components := strings.SplitN(builderID, "/", 3)
    51  	if len(components) != 3 {
    52  		return nil, fmt.Errorf("failed to parse builder ID")
    53  	}
    54  	return &buildbucketpb.BuilderID{
    55  		Project: components[0],
    56  		Bucket:  components[1],
    57  		Builder: components[2],
    58  	}, nil
    59  }
    60  
    61  func getBuildIDForBuilderAndBuildSet(ctx context.Context, host, buildSetID string, builderID *buildbucketpb.BuilderID, buildStatus int32, buildsClient buildbucketpb.BuildsClient) (int64, error) {
    62  	res, err := buildsClient.SearchBuilds(ctx, &buildbucketpb.SearchBuildsRequest{
    63  		Predicate: &buildbucketpb.BuildPredicate{
    64  			Builder: builderID,
    65  			Status:  buildbucketpb.Status(buildStatus),
    66  			Tags: []*buildbucketpb.StringPair{
    67  				{
    68  					Key:   "buildset",
    69  					Value: buildSetID,
    70  				},
    71  			},
    72  		},
    73  	})
    74  	if err != nil {
    75  		return -1, err
    76  	}
    77  	if len(res.Builds) == 0 {
    78  		return -1, fmt.Errorf("No builds returned")
    79  	}
    80  	return res.Builds[0].Id, nil
    81  }
    82  
    83  func main() {
    84  	flag.Parse()
    85  
    86  	if builderID == "" || buildSet == "" {
    87  		flag.PrintDefaults()
    88  		return
    89  	}
    90  
    91  	id, err := parseBuilderID(builderID)
    92  	if err != nil {
    93  		log.Fatalf(err.Error())
    94  	}
    95  
    96  	opts, err := authFlags.Options()
    97  	if err != nil {
    98  		log.Fatalf(err.Error())
    99  	}
   100  
   101  	ctx := context.Background()
   102  	authenticator := auth.NewAuthenticator(ctx, auth.OptionalLogin, opts)
   103  	client, err := authenticator.Client()
   104  	if err != nil {
   105  		log.Fatalf(err.Error())
   106  	}
   107  
   108  	buildsClient := buildbucketpb.NewBuildsPRPCClient(&prpc.Client{
   109  		C:    client,
   110  		Host: host,
   111  	})
   112  
   113  	buildID, err := getBuildIDForBuilderAndBuildSet(ctx, host, buildSet, id, int32(buildStatus), buildsClient)
   114  	if err != nil {
   115  		log.Fatalf(err.Error())
   116  	}
   117  
   118  	var outputFile *os.File
   119  	if output == "" {
   120  		outputFile = os.Stdout
   121  	} else {
   122  		outputFile, err = os.Create(output)
   123  		if err != nil {
   124  			log.Fatalf(err.Error())
   125  		}
   126  		defer outputFile.Close()
   127  	}
   128  	_, err = outputFile.Write([]byte(strconv.FormatInt(buildID, 10)))
   129  	if err != nil {
   130  		log.Fatalf("writing output: %s", err)
   131  	}
   132  }