go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/client/cmd/docker-credential-luci/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 // Command docker-credential-luci is a Docker credential helper. 16 // 17 // The protocol used for communication between Docker and the credential 18 // helper is heavily inspired by Git, but it differs in the information 19 // shared: 20 // 21 // https://docs.docker.com/engine/reference/commandline/login/#credential-helper-protocol 22 package main 23 24 import ( 25 "context" 26 "encoding/json" 27 "flag" 28 "fmt" 29 "os" 30 "time" 31 32 "go.chromium.org/luci/auth" 33 "go.chromium.org/luci/common/logging/gologger" 34 "go.chromium.org/luci/hardcoded/chromeinfra" 35 ) 36 37 func main() { 38 flag.Usage = func() { 39 fmt.Fprintf(os.Stderr, "usage: docker-credential-luci <command>\n") 40 flag.PrintDefaults() 41 } 42 flag.Parse() 43 44 args := flag.Args() 45 if len(args) != 1 { 46 fmt.Fprintf(os.Stderr, "docker-credential-luci: expecting only one argument, got %d\n", len(args)) 47 os.Exit(1) 48 } 49 50 ctx := gologger.StdConfig.Use(context.Background()) 51 52 opts := chromeinfra.DefaultAuthOptions() 53 opts.Scopes = []string{ 54 auth.OAuthScopeEmail, 55 "https://www.googleapis.com/auth/cloud-platform", 56 } 57 auth := auth.NewAuthenticator(ctx, auth.SilentLogin, opts) 58 59 // We only use the command and ignore the payload in stdin. Still need to 60 // close it, otherwise it is possible (though very unlikely) docker CLI will 61 // get stuck writing to a pipe no one is reading. 62 os.Stdin.Close() 63 64 switch args[0] { 65 case "get": 66 t, err := auth.GetAccessToken(3 * time.Minute) 67 if err != nil { 68 printErr("cannot get access token", err) 69 os.Exit(1) 70 } 71 response := map[string]string{ 72 "Username": "oauth2accesstoken", 73 "Secret": t.AccessToken, 74 } 75 if err := json.NewEncoder(os.Stdout).Encode(&response); err != nil { 76 printErr("cannot encode payload", err) 77 os.Exit(1) 78 } 79 case "erase": 80 if err := auth.PurgeCredentialsCache(); err != nil { 81 printErr("cannot erase cache", err) 82 os.Exit(1) 83 } 84 default: 85 // We don't support the "store" and "list" commands and hence ignore them. 86 } 87 } 88 89 func printErr(prefix string, err error) { 90 switch { 91 case err == auth.ErrLoginRequired: 92 fmt.Fprintln(os.Stderr, "docker-credential-luci: not running with a service account and not logged in") 93 case err != nil: 94 fmt.Fprintf(os.Stderr, "docker-credential-luci: %s: %v\n", prefix, err) 95 } 96 }