go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/auth/integration/gsutil/boto.go (about) 1 // Copyright 2019 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 gsutil 16 17 import ( 18 "bytes" 19 "os" 20 "path/filepath" 21 22 "go.chromium.org/luci/common/errors" 23 ) 24 25 // Boto represents a subset of .boto gsutil configuration file. 26 type Boto struct { 27 StateDir string // value of GSUtil.state_dir 28 RefreshToken string // value of Credentials.gs_oauth2_refresh_token 29 GCEServiceAccount string // value of GoogleCompute.service_account 30 ProviderLabel string // value of OAuth2.provider_label 31 ProviderAuthURI string // value of OAuth2.provider_authorization_uri 32 ProviderTokenURI string // value of OAuth2.provider_token_uri 33 } 34 35 // Write creates the config file. 36 func (b *Boto) Write(path string) error { 37 buf := bytes.Buffer{} 38 39 line := func(s string) { 40 buf.WriteString(s) 41 buf.WriteRune('\n') 42 } 43 44 opts := func(name, value string) { 45 if value != "" { 46 buf.WriteString(name) 47 buf.WriteString(" = ") 48 buf.WriteString(value) 49 buf.WriteRune('\n') 50 } 51 } 52 53 line("# Autogenerated by LUCI. Do not edit.") 54 line("") 55 line("[GSUtil]") 56 opts("software_update_check_period", "0") 57 opts("state_dir", b.StateDir) 58 if b.RefreshToken != "" { 59 line("") 60 line("[Credentials]") 61 opts("gs_oauth2_refresh_token", b.RefreshToken) 62 } 63 if b.GCEServiceAccount != "" { 64 line("") 65 line("[GoogleCompute]") 66 opts("service_account", b.GCEServiceAccount) 67 } 68 if b.ProviderLabel != "" || b.ProviderAuthURI != "" || b.ProviderTokenURI != "" { 69 line("") 70 line("[OAuth2]") 71 opts("provider_label", b.ProviderLabel) 72 opts("provider_authorization_uri", b.ProviderAuthURI) 73 opts("provider_token_uri", b.ProviderTokenURI) 74 } 75 76 return os.WriteFile(path, buf.Bytes(), 0600) 77 } 78 79 // PrepareStateDir prepares a directory (based on b.StateDir) for gsutil to keep 80 // its state and drops .boto config there. 81 // 82 // Returns path to the created .boto file. 83 func PrepareStateDir(b *Boto) (string, error) { 84 if err := os.MkdirAll(b.StateDir, 0700); err != nil { 85 return "", errors.Annotate(err, "failed to create gsutil state dir at %s", b.StateDir).Err() 86 } 87 88 botoCfg := filepath.Join(b.StateDir, ".boto") 89 if err := b.Write(botoCfg); err != nil { 90 return "", errors.Annotate(err, "failed to write %s", botoCfg).Err() 91 } 92 93 // Make sure the credentials cache file is empty, otherwise it will grow 94 // after each server launch, since it uses refresh_token (which we may 95 // generate randomly) as a cache key. We don't really need this cache anyway. 96 if err := os.Remove(filepath.Join(b.StateDir, "credstore")); err != nil && !os.IsNotExist(err) { 97 return "", errors.Annotate(err, "failed to remove credstore").Err() 98 } 99 100 return botoCfg, nil 101 }