mirror of https://github.com/docker/cli.git
Fix ConfigFile.Save() replacing symlink with file
In situations where `~/.docker/config.json` was a symlink, saving the file would replace the symlink with a file, instead of updating the target file location; mkdir -p ~/.docker touch ~/real-config.json ln -s ~/real-config.json ~/.docker/config.json ls -la ~/.docker/config.json # lrwxrwxrwx 1 root root 22 Jun 23 12:34 /root/.docker/config.json -> /root/real-config.json docker login # Username: thajeztah # Password: # Login Succeeded ls -la ~/.docker/config.json -rw-r--r-- 1 root root 229 Jun 23 12:36 /root/.docker/config.json Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
03716c0a07
commit
75ab44af6f
|
@ -209,9 +209,15 @@ func (configFile *ConfigFile) Save() (retErr error) {
|
||||||
return errors.Wrap(err, "error closing temp file")
|
return errors.Wrap(err, "error closing temp file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle situation where the configfile is a symlink
|
||||||
|
cfgFile := configFile.Filename
|
||||||
|
if f, err := os.Readlink(cfgFile); err == nil {
|
||||||
|
cfgFile = f
|
||||||
|
}
|
||||||
|
|
||||||
// Try copying the current config file (if any) ownership and permissions
|
// Try copying the current config file (if any) ownership and permissions
|
||||||
copyFilePermissions(configFile.Filename, temp.Name())
|
copyFilePermissions(cfgFile, temp.Name())
|
||||||
return os.Rename(temp.Name(), configFile.Filename)
|
return os.Rename(temp.Name(), cfgFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
|
// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/docker/cli/cli/config/types"
|
"github.com/docker/cli/cli/config/types"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
|
"gotest.tools/v3/fs"
|
||||||
"gotest.tools/v3/golden"
|
"gotest.tools/v3/golden"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -468,6 +469,33 @@ func TestSave(t *testing.T) {
|
||||||
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSaveWithSymlink(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, t.Name(), fs.WithFile("real-config.json", `{}`))
|
||||||
|
defer dir.Remove()
|
||||||
|
|
||||||
|
symLink := dir.Join("config.json")
|
||||||
|
realFile := dir.Join("real-config.json")
|
||||||
|
err := os.Symlink(realFile, symLink)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
configFile := New(symLink)
|
||||||
|
|
||||||
|
err = configFile.Save()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
fi, err := os.Lstat(symLink)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Assert(t, fi.Mode()&os.ModeSymlink != 0, "expected %s to be a symlink", symLink)
|
||||||
|
|
||||||
|
cfg, err := ioutil.ReadFile(symLink)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
||||||
|
|
||||||
|
cfg, err = ioutil.ReadFile(realFile)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestPluginConfig(t *testing.T) {
|
func TestPluginConfig(t *testing.T) {
|
||||||
configFile := New("test-plugin")
|
configFile := New("test-plugin")
|
||||||
defer os.Remove("test-plugin")
|
defer os.Remove("test-plugin")
|
||||||
|
|
Loading…
Reference in New Issue