diff --git a/docker/auth.py b/docker/auth.py index 0a2eda1e..7c1ce761 100644 --- a/docker/auth.py +++ b/docker/auth.py @@ -7,6 +7,7 @@ import dockerpycreds import six from . import errors +from .constants import IS_WINDOWS_PLATFORM INDEX_NAME = 'docker.io' INDEX_URL = 'https://{0}/v1/'.format(INDEX_NAME) @@ -210,19 +211,12 @@ def parse_auth(entries, raise_on_error=False): def find_config_file(config_path=None): - environment_path = os.path.join( - os.environ.get('DOCKER_CONFIG'), - os.path.basename(DOCKER_CONFIG_FILENAME) - ) if os.environ.get('DOCKER_CONFIG') else None - - paths = filter(None, [ + paths = list(filter(None, [ config_path, # 1 - environment_path, # 2 - os.path.join(os.path.expanduser('~'), DOCKER_CONFIG_FILENAME), # 3 - os.path.join( - os.path.expanduser('~'), LEGACY_DOCKER_CONFIG_FILENAME - ) # 4 - ]) + config_path_from_environment(), # 2 + os.path.join(home_dir(), DOCKER_CONFIG_FILENAME), # 3 + os.path.join(home_dir(), LEGACY_DOCKER_CONFIG_FILENAME), # 4 + ])) log.debug("Trying paths: {0}".format(repr(paths))) @@ -236,6 +230,24 @@ def find_config_file(config_path=None): return None +def config_path_from_environment(): + config_dir = os.environ.get('DOCKER_CONFIG') + if not config_dir: + return None + return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME)) + + +def home_dir(): + """ + Get the user's home directory, using the same logic as the Docker Engine + client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX. + """ + if IS_WINDOWS_PLATFORM: + return os.environ.get('USERPROFILE', '') + else: + return os.path.expanduser('~') + + def load_config(config_path=None): """ Loads authentication data from a Docker configuration file in the given diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py index e4c93b78..f9f6fc14 100644 --- a/tests/unit/auth_test.py +++ b/tests/unit/auth_test.py @@ -9,6 +9,9 @@ import shutil import tempfile import unittest +from py.test import ensuretemp +from pytest import mark + from docker import auth, errors try: @@ -269,6 +272,56 @@ class ResolveAuthTest(unittest.TestCase): ) +class FindConfigFileTest(unittest.TestCase): + def tmpdir(self, name): + tmpdir = ensuretemp(name) + self.addCleanup(tmpdir.remove) + return tmpdir + + def test_find_config_fallback(self): + tmpdir = self.tmpdir('test_find_config_fallback') + + with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + assert auth.find_config_file() is None + + def test_find_config_from_explicit_path(self): + tmpdir = self.tmpdir('test_find_config_from_explicit_path') + config_path = tmpdir.ensure('my-config-file.json') + + assert auth.find_config_file(str(config_path)) == str(config_path) + + def test_find_config_from_environment(self): + tmpdir = self.tmpdir('test_find_config_from_environment') + config_path = tmpdir.ensure('config.json') + + with mock.patch.dict(os.environ, {'DOCKER_CONFIG': str(tmpdir)}): + assert auth.find_config_file() == str(config_path) + + @mark.skipif("sys.platform == 'win32'") + def test_find_config_from_home_posix(self): + tmpdir = self.tmpdir('test_find_config_from_home_posix') + config_path = tmpdir.ensure('.docker', 'config.json') + + with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + assert auth.find_config_file() == str(config_path) + + @mark.skipif("sys.platform == 'win32'") + def test_find_config_from_home_legacy_name(self): + tmpdir = self.tmpdir('test_find_config_from_home_legacy_name') + config_path = tmpdir.ensure('.dockercfg') + + with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + assert auth.find_config_file() == str(config_path) + + @mark.skipif("sys.platform != 'win32'") + def test_find_config_from_home_windows(self): + tmpdir = self.tmpdir('test_find_config_from_home_windows') + config_path = tmpdir.ensure('.docker', 'config.json') + + with mock.patch.dict(os.environ, {'USERPROFILE': str(tmpdir)}): + assert auth.find_config_file() == str(config_path) + + class LoadConfigTest(unittest.TestCase): def test_load_config_no_file(self): folder = tempfile.mkdtemp()