golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/swarmclient/config.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package swarmclient
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  
    13  	bbpb "go.chromium.org/luci/buildbucket/proto"
    14  	luciconfig "go.chromium.org/luci/config"
    15  	"go.chromium.org/luci/config/cfgclient"
    16  	"go.chromium.org/luci/config/impl/memory"
    17  	"google.golang.org/grpc/credentials"
    18  	"google.golang.org/protobuf/encoding/prototext"
    19  )
    20  
    21  // ConfigClient is a client for the LUCI configuration service.
    22  type ConfigClient struct {
    23  	config luciconfig.Interface
    24  }
    25  
    26  // NewConfigClient creates a client to access the LUCI configuration service.
    27  func NewConfigClient(ctx context.Context) (*ConfigClient, error) {
    28  	cc, err := cfgclient.New(ctx, cfgclient.Options{
    29  		ServiceHost: "luci-config.appspot.com",
    30  		ClientFactory: func(context.Context) (*http.Client, error) {
    31  			return http.DefaultClient, nil
    32  		},
    33  		GetPerRPCCredsFn: func(context.Context) (credentials.PerRPCCredentials, error) {
    34  			return nil, errors.New("GetPerRPCCredsFn unimplemented")
    35  		},
    36  	})
    37  	if err != nil {
    38  		return nil, fmt.Errorf("cfgclient.New() = nil, %w", err)
    39  	}
    40  	return &ConfigClient{config: cc}, nil
    41  }
    42  
    43  // ConfigEntry represents a configuration file in the configuration directory. It
    44  // should only be used for testing.
    45  type ConfigEntry struct {
    46  	Filename string // name of the file.
    47  	Contents []byte // contents of the configuration file.
    48  }
    49  
    50  // NewMemoryConfigClient creates a config client where the configuration files are stored in memory.
    51  // See https://go.chromium.org/luci/config/impl/filesystem for the expected directory layout.
    52  // This should only be used while testing.
    53  func NewMemoryConfigClient(ctx context.Context, files []*ConfigEntry) *ConfigClient {
    54  	f := make(map[string]string)
    55  	for _, entry := range files {
    56  		f[entry.Filename] = string(entry.Contents)
    57  	}
    58  	cc := memory.New(map[luciconfig.Set]memory.Files{
    59  		luciconfig.Set("projects/golang"): f,
    60  	})
    61  	return &ConfigClient{config: cc}
    62  }
    63  
    64  // SwarmingBot contains the metadata for a LUCI swarming bot.
    65  type SwarmingBot struct {
    66  	// BucketName is the name of the bucket the builder is defined in.
    67  	BucketName string
    68  	// Dimensions contains attributes about the builder. Form is in
    69  	// <key>:<value> or <time>:<key>:<value>
    70  	Dimensions []string
    71  	// Host is the hostname of the swarming instance.
    72  	Host string
    73  	// Name of the builder.
    74  	Name string
    75  }
    76  
    77  // ListSwarmingBots lists all of the swarming bots in the golang project defined in the
    78  // cr-buildbucket.cfg configuration file.
    79  func (cc *ConfigClient) ListSwarmingBots(ctx context.Context) ([]*SwarmingBot, error) {
    80  	bb, err := cc.config.GetConfig(ctx, luciconfig.Set("projects/golang"), "cr-buildbucket.cfg", false)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("client.GetConfig() = nil, %s", err)
    83  	}
    84  	bbc := &bbpb.BuildbucketCfg{}
    85  	if err := prototext.Unmarshal([]byte(bb.Content), bbc); err != nil {
    86  		return nil, fmt.Errorf("prototext.Unmarshal() = %w", err)
    87  	}
    88  	var bots []*SwarmingBot
    89  	for _, bucket := range bbc.GetBuckets() {
    90  		for _, builder := range bucket.GetSwarming().GetBuilders() {
    91  			bots = append(bots, &SwarmingBot{
    92  				BucketName: bucket.GetName(),
    93  				Dimensions: builder.GetDimensions(),
    94  				Host:       builder.GetSwarmingHost(),
    95  				Name:       builder.GetName(),
    96  			})
    97  		}
    98  	}
    99  	return bots, nil
   100  }