blob: cdf0d07adf48c32610d7a9520780952bf9168c24 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
// TODO(nmulcahey): Implement general Search RPC support in the buildbucket
// CLI and remove this + LKGS
import (
"context"
"flag"
"fmt"
"log"
"os"
"strconv"
"strings"
"go.chromium.org/luci/auth"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/grpc/prpc"
"go.chromium.org/luci/auth/client/authcli"
"go.chromium.org/luci/hardcoded/chromeinfra"
)
var (
host string
builderID string
buildSet string
buildStatus int
output string
// LUCI flags used to parse command-line authentication options.
authFlags authcli.Flags
)
func init() {
flag.StringVar(&host, "host", "cr-buildbucket.appspot.com", "the buildbucket host to use (default is cr-buildbucket.appspot.com)")
flag.StringVar(&builderID, "builder-id", "", "name of the builders to use as a reference (e.g. fuchsia/ci/garnet-x64)")
flag.StringVar(&buildSet, "build-set", "", "a unique buildset id in buildbucket (e.g. commit/gitiles/fuchsia.googlesource.com/topaz/+/e3127e0bd6d57da7a5959ee70eb0a396590e6d53)")
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")
flag.StringVar(&output, "output-file", "", "name of the file to write snapshot to (default is stdout)")
authFlags = authcli.Flags{}
authFlags.Register(flag.CommandLine, chromeinfra.DefaultAuthOptions())
}
// praseBuilderID parses a builder ID of the form "fuchsia/ci/garnet-x64".
func parseBuilderID(builderID string) (*buildbucketpb.BuilderID, error) {
components := strings.SplitN(builderID, "/", 3)
if len(components) != 3 {
return nil, fmt.Errorf("failed to parse builder ID")
}
return &buildbucketpb.BuilderID{
Project: components[0],
Bucket: components[1],
Builder: components[2],
}, nil
}
func getBuildIDForBuilderAndBuildSet(ctx context.Context, host, buildSetID string, builderID *buildbucketpb.BuilderID, buildStatus int32, buildsClient buildbucketpb.BuildsClient) (int64, error) {
res, err := buildsClient.SearchBuilds(ctx, &buildbucketpb.SearchBuildsRequest{
Predicate: &buildbucketpb.BuildPredicate{
Builder: builderID,
Status: buildbucketpb.Status(buildStatus),
Tags: []*buildbucketpb.StringPair{
{
Key: "buildset",
Value: buildSetID,
},
},
},
})
if err != nil {
return -1, err
}
if len(res.Builds) == 0 {
return -1, fmt.Errorf("No builds returned")
}
return res.Builds[0].Id, nil
}
func main() {
flag.Parse()
if builderID == "" || buildSet == "" {
flag.PrintDefaults()
return
}
id, err := parseBuilderID(builderID)
if err != nil {
log.Fatalf(err.Error())
}
opts, err := authFlags.Options()
if err != nil {
log.Fatalf(err.Error())
}
ctx := context.Background()
authenticator := auth.NewAuthenticator(ctx, auth.OptionalLogin, opts)
client, err := authenticator.Client()
if err != nil {
log.Fatalf(err.Error())
}
buildsClient := buildbucketpb.NewBuildsPRPCClient(&prpc.Client{
C: client,
Host: host,
})
buildID, err := getBuildIDForBuilderAndBuildSet(ctx, host, buildSet, id, int32(buildStatus), buildsClient)
if err != nil {
log.Fatalf(err.Error())
}
var outputFile *os.File
if output == "" {
outputFile = os.Stdout
} else {
outputFile, err = os.Create(output)
if err != nil {
log.Fatalf(err.Error())
}
defer outputFile.Close()
}
_, err = outputFile.Write([]byte(strconv.FormatInt(buildID, 10)))
if err != nil {
log.Fatalf("writing output: %s", err)
}
}