go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gce/cmd/agent/main.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package main contains an agent for connecting to a Swarming server. 16 package main 17 18 import ( 19 "bytes" 20 "context" 21 "fmt" 22 "os" 23 "text/template" 24 25 "cloud.google.com/go/compute/metadata" 26 "github.com/maruel/subcommands" 27 28 "go.chromium.org/luci/auth" 29 "go.chromium.org/luci/auth/client/authcli" 30 "go.chromium.org/luci/cipd/version" 31 "go.chromium.org/luci/common/cli" 32 "go.chromium.org/luci/common/logging" 33 "go.chromium.org/luci/common/logging/gologger" 34 "go.chromium.org/luci/grpc/prpc" 35 "go.chromium.org/luci/hardcoded/chromeinfra" 36 37 "go.chromium.org/luci/gce/api/instances/v1" 38 "go.chromium.org/luci/gce/vmtoken/client" 39 ) 40 41 // substitute performs substitutions in a template string. 42 func substitute(c context.Context, s string, subs any) (string, error) { 43 t, err := template.New("tmpl").Parse(s) 44 if err != nil { 45 return "", err 46 } 47 buf := bytes.Buffer{} 48 if err = t.Execute(&buf, subs); err != nil { 49 return "", nil 50 } 51 return buf.String(), nil 52 } 53 54 // metaKey is the key to a *metadata.Client in the context. 55 var metaKey = "meta" 56 57 // withMetadata returns a new context with the given *metadata.Client installed. 58 func withMetadata(c context.Context, cli *metadata.Client) context.Context { 59 return context.WithValue(c, &metaKey, cli) 60 } 61 62 // getMetadata returns the *metadata.Client installed in the current context. 63 func getMetadata(c context.Context) *metadata.Client { 64 return c.Value(&metaKey).(*metadata.Client) 65 } 66 67 // newInstances returns a new instances.InstancesClient. 68 func newInstances(c context.Context, acc, host string) (instances.InstancesClient, error) { 69 info, err := version.GetCurrentVersion() 70 if err != nil { 71 return nil, err 72 } 73 options := prpc.DefaultOptions() 74 options.UserAgent = fmt.Sprintf("gce-agent, instanceID=%q", info.InstanceID) 75 return instances.NewInstancesPRPCClient(&prpc.Client{ 76 C: client.NewClient(getMetadata(c), acc), 77 Host: host, 78 Options: options, 79 }), nil 80 } 81 82 // cmdRunBase is the base struct all subcommands should embed. 83 // Implements cli.ContextModificator. 84 type cmdRunBase struct { 85 subcommands.CommandRunBase 86 authFlags authcli.Flags 87 serviceAccount string 88 } 89 90 // Initialize registers common flags. 91 func (b *cmdRunBase) Initialize() { 92 opts := chromeinfra.DefaultAuthOptions() 93 b.authFlags.Register(b.GetFlags(), opts) 94 } 95 96 // ModifyContext returns a new context to be used by all commands. Implements 97 // cli.ContextModificator. 98 func (b *cmdRunBase) ModifyContext(c context.Context) context.Context { 99 c = logging.SetLevel(gologger.StdConfig.Use(c), logging.Debug) 100 opts, err := b.authFlags.Options() 101 if err != nil { 102 logging.Errorf(c, "%s", err.Error()) 103 panic("failed to get auth options") 104 } 105 b.serviceAccount = opts.GCEAccountName 106 http, err := auth.NewAuthenticator(c, auth.OptionalLogin, opts).Client() 107 if err != nil { 108 logging.Errorf(c, "%s", err.Error()) 109 panic("failed to get authenticator") 110 } 111 meta := metadata.NewClient(http) 112 swr := &SwarmingClient{ 113 Client: http, 114 PlatformStrategy: newStrategy(), 115 } 116 return withSwarming(withMetadata(c, meta), swr) 117 } 118 119 // New returns a new agent application. 120 func New() *cli.Application { 121 return &cli.Application{ 122 Name: "agent", 123 Title: "GCE agent", 124 Commands: []*subcommands.Command{ 125 subcommands.CmdHelp, 126 newConnectCmd(), 127 }, 128 } 129 } 130 131 func main() { 132 os.Exit(subcommands.Run(New(), os.Args[1:])) 133 }