blob: da2e7673c738fc562ba36f7d5d458116e235e438 [file] [log] [blame]
// 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
}