2016-12-25 14:31:52 -05:00
package config
import (
2017-06-12 14:36:49 -04:00
"fmt"
2016-12-25 14:31:52 -05:00
"io"
"os"
"path/filepath"
2019-03-07 09:28:42 -05:00
"strings"
2019-10-24 10:11:04 -04:00
"sync"
2016-12-25 14:31:52 -05:00
2017-04-17 18:07:56 -04:00
"github.com/docker/cli/cli/config/configfile"
2017-06-12 14:36:49 -04:00
"github.com/docker/cli/cli/config/credentials"
2017-10-15 15:39:56 -04:00
"github.com/docker/cli/cli/config/types"
2019-09-26 18:12:24 -04:00
"github.com/docker/docker/pkg/homedir"
2017-03-09 13:23:45 -05:00
"github.com/pkg/errors"
2016-12-25 14:31:52 -05:00
)
const (
// ConfigFileName is the name of config file
ConfigFileName = "config.json"
configFileDir = ".docker"
oldConfigfile = ".dockercfg"
2018-12-17 05:27:07 -05:00
contextsDir = "contexts"
2016-12-25 14:31:52 -05:00
)
var (
2020-07-16 06:01:54 -04:00
initConfigDir = new ( sync . Once )
2019-10-24 10:11:04 -04:00
configDir string
2021-01-18 08:46:52 -05:00
homeDir string
2016-12-25 14:31:52 -05:00
)
2020-07-16 06:01:54 -04:00
// resetHomeDir is used in testing to reset the "homeDir" package variable to
2021-01-18 08:46:52 -05:00
// force re-lookup of the home directory between tests.
func resetHomeDir ( ) {
homeDir = ""
}
func getHomeDir ( ) string {
if homeDir == "" {
homeDir = homedir . Get ( )
}
return homeDir
}
2020-07-16 06:01:54 -04:00
// resetConfigDir is used in testing to reset the "configDir" package variable
// and its sync.Once to force re-lookup between tests.
func resetConfigDir ( ) {
configDir = ""
initConfigDir = new ( sync . Once )
}
2019-10-24 10:11:04 -04:00
func setConfigDir ( ) {
if configDir != "" {
return
}
configDir = os . Getenv ( "DOCKER_CONFIG" )
2016-12-25 14:31:52 -05:00
if configDir == "" {
2021-01-18 08:46:52 -05:00
configDir = filepath . Join ( getHomeDir ( ) , configFileDir )
2016-12-25 14:31:52 -05:00
}
}
// Dir returns the directory the configuration file is stored in
func Dir ( ) string {
2019-10-24 10:11:04 -04:00
initConfigDir . Do ( setConfigDir )
2016-12-25 14:31:52 -05:00
return configDir
}
2018-12-17 05:27:07 -05:00
// ContextStoreDir returns the directory the docker contexts are stored in
func ContextStoreDir ( ) string {
return filepath . Join ( Dir ( ) , contextsDir )
}
2016-12-25 14:31:52 -05:00
// SetDir sets the directory the configuration file is stored in
func SetDir ( dir string ) {
2019-03-07 09:28:42 -05:00
configDir = filepath . Clean ( dir )
2016-12-25 14:31:52 -05:00
}
2019-01-10 10:49:06 -05:00
// Path returns the path to a file relative to the config dir
2019-03-07 09:28:42 -05:00
func Path ( p ... string ) ( string , error ) {
path := filepath . Join ( append ( [ ] string { Dir ( ) } , p ... ) ... )
if ! strings . HasPrefix ( path , Dir ( ) + string ( filepath . Separator ) ) {
return "" , errors . Errorf ( "path %q is outside of root config directory %q" , path , Dir ( ) )
}
return path , nil
2019-01-10 10:49:06 -05:00
}
2016-12-25 14:31:52 -05:00
// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
// a non-nested reader
func LegacyLoadFromReader ( configData io . Reader ) ( * configfile . ConfigFile , error ) {
configFile := configfile . ConfigFile {
AuthConfigs : make ( map [ string ] types . AuthConfig ) ,
}
err := configFile . LegacyLoadFromReader ( configData )
return & configFile , err
}
// LoadFromReader is a convenience function that creates a ConfigFile object from
// a reader
func LoadFromReader ( configData io . Reader ) ( * configfile . ConfigFile , error ) {
configFile := configfile . ConfigFile {
AuthConfigs : make ( map [ string ] types . AuthConfig ) ,
}
err := configFile . LoadFromReader ( configData )
return & configFile , err
}
2020-07-16 06:01:54 -04:00
// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
var (
mutex sync . RWMutex
printLegacyFileWarning bool
)
2016-12-25 14:31:52 -05:00
// Load reads the configuration files in the given directory, and sets up
// the auth config information and returns values.
// FIXME: use the internal golang config parser
func Load ( configDir string ) ( * configfile . ConfigFile , error ) {
2020-07-16 06:01:54 -04:00
mutex . Lock ( )
printLegacyFileWarning = false
mutex . Unlock ( )
2016-12-25 14:31:52 -05:00
if configDir == "" {
configDir = Dir ( )
}
2017-06-22 12:03:58 -04:00
filename := filepath . Join ( configDir , ConfigFileName )
2017-06-27 10:31:38 -04:00
configFile := configfile . New ( filename )
2016-12-25 14:31:52 -05:00
// Try happy path first - latest config file
2020-06-15 04:41:28 -04:00
if file , err := os . Open ( filename ) ; err == nil {
2016-12-25 14:31:52 -05:00
defer file . Close ( )
err = configFile . LoadFromReader ( file )
if err != nil {
2018-03-06 13:44:06 -05:00
err = errors . Wrap ( err , filename )
2016-12-25 14:31:52 -05:00
}
2017-06-22 12:03:58 -04:00
return configFile , err
2016-12-25 14:31:52 -05:00
} else if ! os . IsNotExist ( err ) {
// if file is there but we can't stat it for any reason other
// than it doesn't exist then stop
2018-03-06 13:44:06 -05:00
return configFile , errors . Wrap ( err , filename )
2016-12-25 14:31:52 -05:00
}
// Can't find latest config file so check for the old one
2021-01-18 08:46:52 -05:00
filename = filepath . Join ( getHomeDir ( ) , oldConfigfile )
2020-06-15 04:41:28 -04:00
if file , err := os . Open ( filename ) ; err == nil {
2020-07-16 06:01:54 -04:00
mutex . Lock ( )
printLegacyFileWarning = true
mutex . Unlock ( )
2020-06-15 04:41:28 -04:00
defer file . Close ( )
if err := configFile . LegacyLoadFromReader ( file ) ; err != nil {
return configFile , errors . Wrap ( err , filename )
}
2016-12-25 14:31:52 -05:00
}
2017-06-22 12:03:58 -04:00
return configFile , nil
2016-12-25 14:31:52 -05:00
}
2017-06-12 14:36:49 -04:00
// LoadDefaultConfigFile attempts to load the default config file and returns
// an initialized ConfigFile struct if none is found.
2017-06-21 17:20:49 -04:00
func LoadDefaultConfigFile ( stderr io . Writer ) * configfile . ConfigFile {
configFile , err := Load ( Dir ( ) )
if err != nil {
fmt . Fprintf ( stderr , "WARNING: Error loading config file: %v\n" , err )
2017-06-12 14:36:49 -04:00
}
2020-07-16 06:01:54 -04:00
mutex . RLock ( )
defer mutex . RUnlock ( )
if printLegacyFileWarning {
_ , _ = fmt . Fprintln ( stderr , "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release" )
}
2017-06-12 14:36:49 -04:00
if ! configFile . ContainsAuth ( ) {
2017-06-21 16:47:06 -04:00
configFile . CredentialsStore = credentials . DetectDefaultStore ( configFile . CredentialsStore )
2017-06-12 14:36:49 -04:00
}
return configFile
}