config/file.go (133 lines of code) (raw):

// Copyright 2016 Google, Inc. // // 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 config import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "github.com/GoogleCloudPlatform/docker-credential-gcr/v2/util" ) const ( configFileEnvVariable = "DOCKER_CREDENTIAL_GCR_CONFIG" configFileName = "docker_credential_gcr_config.json" ) // DefaultTokenSources designates which default source(s) should be used to // fetch a GCR access_token, and in which order. var DefaultTokenSources = [...]string{"store", "env"} // UserConfig describes type UserConfig interface { TokenSources() []string SetTokenSources([]string) error ResetAll() error } // configFile describes the structure of the persistent config store. type configFile struct { TokenSrcs []string `json:"TokenSources,omitempty"` // package private helper, made a member variable and exposed for testing persist func(*configFile) error } // LoadUserConfig returns the UserConfig which provides user-configurable // application settings, or a new on if it doesn't exist. func LoadUserConfig() (UserConfig, error) { config, err := load() if err != nil { if !os.IsNotExist(err) { return nil, err } config = &configFile{} } config.persist = persist return config, nil } func load() (*configFile, error) { path, err := configPath() if err != nil { return nil, err } f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() var config configFile if err := json.NewDecoder(f).Decode(&config); err != nil { return nil, fmt.Errorf("failed to load config from %s: %v", path, err) } return &config, nil } // TokenSources returns the configured token sources, or the DefaultTokenSources // if none are set. func (c *configFile) TokenSources() []string { if len(c.TokenSrcs) == 0 { // if nil or empty return DefaultTokenSources[:] } ret := make([]string, len(c.TokenSrcs)) copy(ret, c.TokenSrcs) return ret } // SetTokenSources sets (and persists) the token sources. Valid token sources // are defined by config.SupportedGCRTokenSources. func (c *configFile) SetTokenSources(newSources []string) error { if len(newSources) == 0 { newSources = nil } // Don't touch the file unless we need to. if equal(newSources, c.TokenSrcs) { return nil } for _, source := range newSources { if _, supported := SupportedGCRTokenSources[source]; !supported { return fmt.Errorf("Unsupported token source: %s", source) } } c.TokenSrcs = newSources return c.persist(c) } func persist(c *configFile) error { f, err := createConfigFile() if err != nil { return err } defer f.Close() return json.NewEncoder(f).Encode(c) } func equal(a, b []string) bool { if a == nil && b == nil { return true } if a == nil || b == nil { return false } if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } // ResetAll clears all user configuration. func (c *configFile) ResetAll() error { err := deleteConfigFile() if err != nil { return err } c.TokenSrcs = nil return nil } func deleteConfigFile() error { path, err := configPath() if err != nil && !os.IsNotExist(err) { return err } return os.Remove(path) } // createConfigFile creates (or truncates) and returns an os.File for the // user config. func createConfigFile() (*os.File, error) { path, err := configPath() if err != nil { return nil, err } // create the gcloud config dir, if it doesnt exist if err = os.MkdirAll(filepath.Dir(path), 0777); err != nil { return nil, err } // create or truncate the config file and return it return os.Create(path) } // configPath returns the full path of our user config file. func configPath() (string, error) { if path := os.Getenv(configFileEnvVariable); strings.TrimSpace(path) != "" { return path, nil } sdkConfigPath, err := util.SdkConfigPath() if err != nil { return "", fmt.Errorf("couldn't construct config path: %v", err) } return filepath.Join(sdkConfigPath, configFileName), nil }