| // Copyright 2019 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 gsutil |
| |
| import ( |
| "bytes" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| |
| "go.chromium.org/luci/common/errors" |
| ) |
| |
| // Boto represents a subset of .boto gsutil configuration file. |
| type Boto struct { |
| StateDir string // value of GSUtil.state_dir |
| RefreshToken string // value of Credentials.gs_oauth2_refresh_token |
| GCEServiceAccount string // value of GoogleCompute.service_account |
| ProviderLabel string // value of OAuth2.provider_label |
| ProviderAuthURI string // value of OAuth2.provider_authorization_uri |
| ProviderTokenURI string // value of OAuth2.provider_token_uri |
| } |
| |
| // Write creates the config file. |
| func (b *Boto) Write(path string) error { |
| buf := bytes.Buffer{} |
| |
| line := func(s string) { |
| buf.WriteString(s) |
| buf.WriteRune('\n') |
| } |
| |
| opts := func(name, value string) { |
| if value != "" { |
| buf.WriteString(name) |
| buf.WriteString(" = ") |
| buf.WriteString(value) |
| buf.WriteRune('\n') |
| } |
| } |
| |
| line("# Autogenerated by LUCI. Do not edit.") |
| line("") |
| line("[GSUtil]") |
| opts("software_update_check_period", "0") |
| opts("state_dir", b.StateDir) |
| if b.RefreshToken != "" { |
| line("") |
| line("[Credentials]") |
| opts("gs_oauth2_refresh_token", b.RefreshToken) |
| } |
| if b.GCEServiceAccount != "" { |
| line("") |
| line("[GoogleCompute]") |
| opts("service_account", b.GCEServiceAccount) |
| } |
| if b.ProviderLabel != "" || b.ProviderAuthURI != "" || b.ProviderTokenURI != "" { |
| line("") |
| line("[OAuth2]") |
| opts("provider_label", b.ProviderLabel) |
| opts("provider_authorization_uri", b.ProviderAuthURI) |
| opts("provider_token_uri", b.ProviderTokenURI) |
| } |
| |
| return ioutil.WriteFile(path, buf.Bytes(), 0600) |
| } |
| |
| // PrepareStateDir prepares a directory (based on b.StateDir) for gsutil to keep |
| // its state and drops .boto config there. |
| // |
| // Returns path to the created .boto file. |
| func PrepareStateDir(b *Boto) (string, error) { |
| if err := os.MkdirAll(b.StateDir, 0700); err != nil { |
| return "", errors.Annotate(err, "failed to create gsutil state dir at %s", b.StateDir).Err() |
| } |
| |
| botoCfg := filepath.Join(b.StateDir, ".boto") |
| if err := b.Write(botoCfg); err != nil { |
| return "", errors.Annotate(err, "failed to write %s", botoCfg).Err() |
| } |
| |
| // Make sure the credentials cache file is empty, otherwise it will grow |
| // after each server launch, since it uses refresh_token (which we may |
| // generate randomly) as a cache key. We don't really need this cache anyway. |
| if err := os.Remove(filepath.Join(b.StateDir, "credstore")); err != nil && !os.IsNotExist(err) { |
| return "", errors.Annotate(err, "failed to remove credstore").Err() |
| } |
| |
| return botoCfg, nil |
| } |