@@ -75,7 +75,7 @@ func Path(p ...string) (string, error) {
75
75
}
76
76
77
77
// LoadFromReader is a convenience function that creates a ConfigFile object from
78
- // a reader
78
+ // a reader. It returns an error if configData is malformed.
79
79
func LoadFromReader (configData io.Reader ) (* configfile.ConfigFile , error ) {
80
80
configFile := configfile.ConfigFile {
81
81
AuthConfigs : make (map [string ]types.AuthConfig ),
@@ -88,6 +88,10 @@ func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
88
88
// If no directory is given, it uses the default [Dir]. A [configfile.ConfigFile]
89
89
// is returned containing the contents of the configuration file, or a default
90
90
// struct if no configfile exists in the given location.
91
+ //
92
+ // Load returns an error if a configuration file exists in the given location,
93
+ // but cannot be read, or is malformed. Consumers must handle errors to prevent
94
+ // overwriting an existing configuration file.
91
95
func Load (configDir string ) (* configfile.ConfigFile , error ) {
92
96
if configDir == "" {
93
97
configDir = Dir ()
@@ -102,29 +106,36 @@ func load(configDir string) (*configfile.ConfigFile, error) {
102
106
file , err := os .Open (filename )
103
107
if err != nil {
104
108
if os .IsNotExist (err ) {
105
- //
106
- // if file is there but we can't stat it for any reason other
107
- // than it doesn't exist then stop
109
+ // It is OK for no configuration file to be present, in which
110
+ // case we return a default struct.
108
111
return configFile , nil
109
112
}
110
- // if file is there but we can't stat it for any reason other
111
- // than it doesn't exist then stop
112
- return configFile , nil
113
+ // Any other error happening when failing to read the file must be returned.
114
+ return configFile , errors .Wrap (err , "loading config file" )
113
115
}
114
116
defer file .Close ()
115
117
err = configFile .LoadFromReader (file )
116
118
if err != nil {
117
- err = errors .Wrap (err , filename )
119
+ err = errors .Wrapf (err , "loading config file: %s: " , filename )
118
120
}
119
121
return configFile , err
120
122
}
121
123
122
124
// LoadDefaultConfigFile attempts to load the default config file and returns
123
- // an initialized ConfigFile struct if none is found.
125
+ // an initialized ConfigFile struct if none is found or when failing to load
126
+ // the configuration file. If no credentials-store is set in the configuration
127
+ // file, it attempts to discover the default store to use for the current platform.
128
+ //
129
+ // Important: LoadDefaultConfigFile prints a warning to stderr when failing to
130
+ // load the configuration file, but otherwise ignores errors. Consumers should
131
+ // consider using [Load] (and [credentials.DetectDefaultStore]) to detect errors
132
+ // when updating the configuration file, to prevent discarding a (malformed)
133
+ // configuration file.
124
134
func LoadDefaultConfigFile (stderr io.Writer ) * configfile.ConfigFile {
125
135
configFile , err := load (Dir ())
126
136
if err != nil {
127
- _ , _ = fmt .Fprintf (stderr , "WARNING: Error loading config file: %v\n " , err )
137
+ // FIXME(thaJeztah): we should not proceed here to prevent overwriting existing (but malformed) config files; see https://github.com/docker/cli/issues/5075
138
+ _ , _ = fmt .Fprintln (stderr , "WARNING: Error" , err )
128
139
}
129
140
if ! configFile .ContainsAuth () {
130
141
configFile .CredentialsStore = credentials .DetectDefaultStore (configFile .CredentialsStore )
0 commit comments