blob: 03c9f168aa5882e9461ecff73a5ab62cfd7e4ccd [file] [log] [blame]
// Copyright 2023 The Go 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 swarmclient
import (
"context"
"errors"
"fmt"
"net/http"
bbpb "go.chromium.org/luci/buildbucket/proto"
luciconfig "go.chromium.org/luci/config"
"go.chromium.org/luci/config/cfgclient"
"go.chromium.org/luci/config/impl/memory"
"google.golang.org/grpc/credentials"
"google.golang.org/protobuf/encoding/prototext"
)
// ConfigClient is a client for the LUCI configuration service.
type ConfigClient struct {
config luciconfig.Interface
}
// NewConfigClient creates a client to access the LUCI configuration service.
func NewConfigClient(ctx context.Context) (*ConfigClient, error) {
cc, err := cfgclient.New(ctx, cfgclient.Options{
ServiceHost: "luci-config.appspot.com",
ClientFactory: func(context.Context) (*http.Client, error) {
return http.DefaultClient, nil
},
GetPerRPCCredsFn: func(context.Context) (credentials.PerRPCCredentials, error) {
return nil, errors.New("GetPerRPCCredsFn unimplemented")
},
})
if err != nil {
return nil, fmt.Errorf("cfgclient.New() = nil, %w", err)
}
return &ConfigClient{config: cc}, nil
}
// ConfigEntry represents a configuration file in the configuration directory. It
// should only be used for testing.
type ConfigEntry struct {
Filename string // name of the file.
Contents []byte // contents of the configuration file.
}
// NewMemoryConfigClient creates a config client where the configuration files are stored in memory.
// See https://go.chromium.org/luci/config/impl/filesystem for the expected directory layout.
// This should only be used while testing.
func NewMemoryConfigClient(ctx context.Context, files []*ConfigEntry) *ConfigClient {
f := make(map[string]string)
for _, entry := range files {
f[entry.Filename] = string(entry.Contents)
}
cc := memory.New(map[luciconfig.Set]memory.Files{
luciconfig.Set("projects/golang"): f,
})
return &ConfigClient{config: cc}
}
// SwarmingBot contains the metadata for a LUCI swarming bot.
type SwarmingBot struct {
// BucketName is the name of the bucket the builder is defined in.
BucketName string
// Dimensions contains attributes about the builder. Form is in
// <key>:<value> or <time>:<key>:<value>
Dimensions []string
// Host is the hostname of the swarming instance.
Host string
// Name of the builder.
Name string
}
// ListSwarmingBots lists all of the swarming bots in the golang project defined in the
// cr-buildbucket.cfg configuration file.
func (cc *ConfigClient) ListSwarmingBots(ctx context.Context) ([]*SwarmingBot, error) {
bb, err := cc.config.GetConfig(ctx, luciconfig.Set("projects/golang"), "cr-buildbucket.cfg", false)
if err != nil {
return nil, fmt.Errorf("client.GetConfig() = nil, %s", err)
}
bbc := &bbpb.BuildbucketCfg{}
if err := prototext.Unmarshal([]byte(bb.Content), bbc); err != nil {
return nil, fmt.Errorf("prototext.Unmarshal() = %w", err)
}
var bots []*SwarmingBot
for _, bucket := range bbc.GetBuckets() {
for _, builder := range bucket.GetSwarming().GetBuilders() {
bots = append(bots, &SwarmingBot{
BucketName: bucket.GetName(),
Dimensions: builder.GetDimensions(),
Host: builder.GetSwarmingHost(),
Name: builder.GetName(),
})
}
}
return bots, nil
}