diff --git a/docker/context/config.py b/docker/context/config.py index 5a6373aa4e..61cb900ea7 100644 --- a/docker/context/config.py +++ b/docker/context/config.py @@ -4,11 +4,23 @@ from docker import utils from docker.constants import DEFAULT_UNIX_SOCKET, IS_WINDOWS_PLATFORM -from docker.utils.config import find_config_file +from docker.utils.config import ( + DOCKER_CONFIG_FILENAME, + config_path_from_environment, + find_config_file, + home_dir, +) METAFILE = "meta.json" +def _default_docker_config_path(): + return ( + config_path_from_environment() + or os.path.join(home_dir(), DOCKER_CONFIG_FILENAME) + ) + + def get_current_context_name(): name = "default" docker_cfg_path = find_config_file() @@ -24,9 +36,9 @@ def get_current_context_name(): def write_context_name_to_docker_config(name=None): if name == 'default': name = None - docker_cfg_path = find_config_file() + docker_cfg_path = find_config_file() or _default_docker_config_path() config = {} - if docker_cfg_path: + if os.path.exists(docker_cfg_path): try: with open(docker_cfg_path) as f: config = json.load(f) @@ -40,6 +52,7 @@ def write_context_name_to_docker_config(name=None): else: return try: + os.makedirs(os.path.dirname(docker_cfg_path), exist_ok=True) with open(docker_cfg_path, "w") as f: json.dump(config, f, indent=4) except Exception as e: @@ -51,7 +64,10 @@ def get_context_id(name): def get_context_dir(): - return os.path.join(os.path.dirname(find_config_file() or ""), "contexts") + return os.path.join( + os.path.dirname(find_config_file() or _default_docker_config_path()), + "contexts", + ) def get_meta_dir(name=None): diff --git a/tests/unit/context_test.py b/tests/unit/context_test.py index 9e9fc9ba13..030d5fe8e1 100644 --- a/tests/unit/context_test.py +++ b/tests/unit/context_test.py @@ -1,10 +1,15 @@ +import json +import os +import tempfile import unittest +from unittest import mock import pytest import docker from docker.constants import DEFAULT_NPIPE, DEFAULT_UNIX_SOCKET, IS_WINDOWS_PLATFORM from docker.context import Context, ContextAPI +from docker.context.config import get_context_dir, get_meta_dir, get_tls_dir class BaseContextTest(unittest.TestCase): @@ -49,3 +54,37 @@ def test_context_inspect_without_params(self): DEFAULT_NPIPE, DEFAULT_UNIX_SOCKET[5:], ) + + +@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='POSIX-specific HOME handling') +def test_context_paths_use_default_docker_config_dir_when_config_missing(): + with tempfile.TemporaryDirectory() as home: + with mock.patch.dict(os.environ, {'HOME': home}, clear=False): + assert get_context_dir() == os.path.join(home, '.docker', 'contexts') + assert get_meta_dir('demo').startswith( + os.path.join(home, '.docker', 'contexts', 'meta') + ) + assert get_tls_dir('demo').startswith( + os.path.join(home, '.docker', 'contexts', 'tls') + ) + + +@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='POSIX-specific HOME handling') +def test_set_current_context_creates_config_in_default_docker_dir(): + with tempfile.TemporaryDirectory() as home, tempfile.TemporaryDirectory() as cwd: + with mock.patch.dict(os.environ, {'HOME': home}, clear=False): + old_cwd = os.getcwd() + try: + os.chdir(cwd) + ctx = ContextAPI.create_context('demo') + ContextAPI.set_current_context('demo') + finally: + os.chdir(old_cwd) + + config_path = os.path.join(home, '.docker', 'config.json') + with open(config_path) as f: + config = json.load(f) + + assert config["currentContext"] == 'demo' + assert ctx.meta_path.startswith(os.path.join(home, '.docker', 'contexts')) + assert not os.path.exists(os.path.join(cwd, 'contexts'))