diff --git a/docker/lookaside.go b/docker/lookaside.go index 918c0f838..6931fd07b 100644 --- a/docker/lookaside.go +++ b/docker/lookaside.go @@ -11,6 +11,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" "github.com/ghodss/yaml" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -26,6 +27,9 @@ var systemRegistriesDirPath = builtinRegistriesDirPath // DO NOT change this, instead see systemRegistriesDirPath above. const builtinRegistriesDirPath = "/etc/containers/registries.d" +// userRegistriesDirPath is the path to the per user registries.d. +var userRegistriesDir = filepath.FromSlash(".config/containers/registries.d") + // registryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all. // NOTE: Keep this in sync with docs/registries.d.md! type registryConfiguration struct { @@ -75,14 +79,17 @@ func configuredSignatureStorageBase(sys *types.SystemContext, ref dockerReferenc // registriesDirPath returns a path to registries.d func registriesDirPath(sys *types.SystemContext) string { - if sys != nil { - if sys.RegistriesDirPath != "" { - return sys.RegistriesDirPath - } - if sys.RootForImplicitAbsolutePaths != "" { - return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath) - } + if sys != nil && sys.RegistriesDirPath != "" { + return sys.RegistriesDirPath } + userRegistriesDirPath := filepath.Join(homedir.Get(), userRegistriesDir) + if _, err := os.Stat(userRegistriesDirPath); err == nil { + return userRegistriesDirPath + } + if sys != nil && sys.RootForImplicitAbsolutePaths != "" { + return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath) + } + return systemRegistriesDirPath } diff --git a/docker/lookaside_test.go b/docker/lookaside_test.go index e72a23ee7..7d9caef61 100644 --- a/docker/lookaside_test.go +++ b/docker/lookaside_test.go @@ -53,20 +53,36 @@ func TestRegistriesDirPath(t *testing.T) { const nondefaultPath = "/this/is/not/the/default/registries.d" const variableReference = "$HOME" const rootPrefix = "/root/prefix" - + oldHomeEnv, hasHomeEnv := os.LookupEnv("HOME") + tempHome, err := ioutil.TempDir("", "tempHome") + require.NoError(t, err) + err = os.Setenv("HOME", tempHome) + require.NoError(t, err) + defer func() { + os.RemoveAll(tempHome) + if hasHomeEnv { + os.Setenv("HOME", oldHomeEnv) + } else { + os.Unsetenv("HOME") + } + }() + var userRegistriesDir = filepath.FromSlash(".config/containers/registries.d") + userRegistriesDirPath := filepath.Join(tempHome, userRegistriesDir) for _, c := range []struct { - sys *types.SystemContext - expected string + sys *types.SystemContext + userFilePresent bool + expected string }{ // The common case - {nil, systemRegistriesDirPath}, + {nil, false, systemRegistriesDirPath}, // There is a context, but it does not override the path. - {&types.SystemContext{}, systemRegistriesDirPath}, + {&types.SystemContext{}, false, systemRegistriesDirPath}, // Path overridden - {&types.SystemContext{RegistriesDirPath: nondefaultPath}, nondefaultPath}, + {&types.SystemContext{RegistriesDirPath: nondefaultPath}, false, nondefaultPath}, // Root overridden { &types.SystemContext{RootForImplicitAbsolutePaths: rootPrefix}, + false, filepath.Join(rootPrefix, systemRegistriesDirPath), }, // Root and path overrides present simultaneously, @@ -75,11 +91,32 @@ func TestRegistriesDirPath(t *testing.T) { RootForImplicitAbsolutePaths: rootPrefix, RegistriesDirPath: nondefaultPath, }, + false, + nondefaultPath, + }, + // User registries.d present, not overridden + {&types.SystemContext{}, true, userRegistriesDirPath}, + // Context and user User registries.d preset simultaneously + {&types.SystemContext{RegistriesDirPath: nondefaultPath}, true, nondefaultPath}, + // Root and user registries.d overrides present simultaneously, + { + &types.SystemContext{ + RootForImplicitAbsolutePaths: rootPrefix, + RegistriesDirPath: nondefaultPath, + }, + true, nondefaultPath, }, // No environment expansion happens in the overridden paths - {&types.SystemContext{RegistriesDirPath: variableReference}, variableReference}, + {&types.SystemContext{RegistriesDirPath: variableReference}, false, variableReference}, } { + if c.userFilePresent { + err := os.MkdirAll(userRegistriesDirPath, 0700) + require.NoError(t, err) + } else { + err := os.RemoveAll(userRegistriesDirPath) + require.NoError(t, err) + } path := registriesDirPath(c.sys) assert.Equal(t, c.expected, path) } diff --git a/docs/containers-registries.d.5.md b/docs/containers-registries.d.5.md index 263002b13..892f617d6 100644 --- a/docs/containers-registries.d.5.md +++ b/docs/containers-registries.d.5.md @@ -12,7 +12,7 @@ The registries configuration directory contains configuration for various regist so that the configuration does not have to be provided in command-line options over and over for every command, and so that it can be shared by all users of containers/image. -By default (unless overridden at compile-time), the registries configuration directory is `/etc/containers/registries.d`; +By default, the registries configuration directory is `$HOME/.config/containers/registries.d` if it exists, otherwise `/etc/containers/registries.d` (unless overridden at compile-time); applications may allow using a different directory instead. ## Directory Structure