| // Copyright 2018 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 main contains an agent for connecting to a Swarming server. |
| package main |
| |
| import ( |
| "bytes" |
| "context" |
| "fmt" |
| "os" |
| "text/template" |
| |
| "cloud.google.com/go/compute/metadata" |
| "github.com/maruel/subcommands" |
| |
| "go.chromium.org/luci/auth" |
| "go.chromium.org/luci/auth/client/authcli" |
| "go.chromium.org/luci/cipd/version" |
| "go.chromium.org/luci/common/cli" |
| "go.chromium.org/luci/common/logging" |
| "go.chromium.org/luci/common/logging/gologger" |
| "go.chromium.org/luci/grpc/prpc" |
| "go.chromium.org/luci/hardcoded/chromeinfra" |
| |
| "go.chromium.org/luci/gce/api/instances/v1" |
| "go.chromium.org/luci/gce/vmtoken/client" |
| ) |
| |
| // substitute performs substitutions in a template string. |
| func substitute(c context.Context, s string, subs any) (string, error) { |
| t, err := template.New("tmpl").Parse(s) |
| if err != nil { |
| return "", err |
| } |
| buf := bytes.Buffer{} |
| if err = t.Execute(&buf, subs); err != nil { |
| return "", nil |
| } |
| return buf.String(), nil |
| } |
| |
| // metaKey is the key to a *metadata.Client in the context. |
| var metaKey = "meta" |
| |
| // withMetadata returns a new context with the given *metadata.Client installed. |
| func withMetadata(c context.Context, cli *metadata.Client) context.Context { |
| return context.WithValue(c, &metaKey, cli) |
| } |
| |
| // getMetadata returns the *metadata.Client installed in the current context. |
| func getMetadata(c context.Context) *metadata.Client { |
| return c.Value(&metaKey).(*metadata.Client) |
| } |
| |
| // newInstances returns a new instances.InstancesClient. |
| func newInstances(c context.Context, acc, host string) (instances.InstancesClient, error) { |
| info, err := version.GetCurrentVersion() |
| if err != nil { |
| return nil, err |
| } |
| options := prpc.DefaultOptions() |
| options.UserAgent = fmt.Sprintf("gce-agent, instanceID=%q", info.InstanceID) |
| return instances.NewInstancesPRPCClient(&prpc.Client{ |
| C: client.NewClient(getMetadata(c), acc), |
| Host: host, |
| Options: options, |
| }), nil |
| } |
| |
| // cmdRunBase is the base struct all subcommands should embed. |
| // Implements cli.ContextModificator. |
| type cmdRunBase struct { |
| subcommands.CommandRunBase |
| authFlags authcli.Flags |
| serviceAccount string |
| } |
| |
| // Initialize registers common flags. |
| func (b *cmdRunBase) Initialize() { |
| opts := chromeinfra.DefaultAuthOptions() |
| b.authFlags.Register(b.GetFlags(), opts) |
| } |
| |
| // ModifyContext returns a new context to be used by all commands. Implements |
| // cli.ContextModificator. |
| func (b *cmdRunBase) ModifyContext(c context.Context) context.Context { |
| c = logging.SetLevel(gologger.StdConfig.Use(c), logging.Debug) |
| opts, err := b.authFlags.Options() |
| if err != nil { |
| logging.Errorf(c, "%s", err.Error()) |
| panic("failed to get auth options") |
| } |
| b.serviceAccount = opts.GCEAccountName |
| http, err := auth.NewAuthenticator(c, auth.OptionalLogin, opts).Client() |
| if err != nil { |
| logging.Errorf(c, "%s", err.Error()) |
| panic("failed to get authenticator") |
| } |
| meta := metadata.NewClient(http) |
| swr := &SwarmingClient{ |
| Client: http, |
| PlatformStrategy: newStrategy(), |
| } |
| return withSwarming(withMetadata(c, meta), swr) |
| } |
| |
| // New returns a new agent application. |
| func New() *cli.Application { |
| return &cli.Application{ |
| Name: "agent", |
| Title: "GCE agent", |
| Commands: []*subcommands.Command{ |
| subcommands.CmdHelp, |
| newConnectCmd(), |
| }, |
| } |
| } |
| |
| func main() { |
| os.Exit(subcommands.Run(New(), os.Args[1:])) |
| } |