From e1baff04eb50705124ea129c9b5dcfbcf9dad1dd Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 6 Feb 2025 13:16:23 +0100 Subject: [PATCH] Adjust more to behavior of Docker CLI. --- plugins/module_utils/_api/context/api.py | 30 +++++++++++++++---- plugins/module_utils/_api/context/config.py | 4 +++ plugins/module_utils/_api/context/context.py | 13 +++++--- plugins/module_utils/_api/utils/utils.py | 7 +---- .../module_utils/_api/utils/test_utils.py | 2 +- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/plugins/module_utils/_api/context/api.py b/plugins/module_utils/_api/context/api.py index 620ff288..eb54e433 100644 --- a/plugins/module_utils/_api/context/api.py +++ b/plugins/module_utils/_api/context/api.py @@ -26,12 +26,27 @@ from .config import ( from .context import Context +def create_default_context(): + host = None + if os.environ.get('DOCKER_HOST'): + host = os.environ.get('DOCKER_HOST') + return Context("default", "swarm", host, description="Current DOCKER_HOST based configuration") + + class ContextAPI(object): """Context API. Contains methods for context management: create, list, remove, get, inspect. """ - DEFAULT_CONTEXT = Context("default", "swarm") + DEFAULT_CONTEXT = None + + @classmethod + def get_default_context(cls): + context = cls.DEFAULT_CONTEXT + if context is None: + context = create_default_context() + cls.DEFAULT_CONTEXT = context + return context @classmethod def create_context( @@ -108,7 +123,7 @@ class ContextAPI(object): if not name: name = get_current_context_name() if name == "default": - return cls.DEFAULT_CONTEXT + return cls.get_default_context() return Context.load_context(name) @classmethod @@ -128,13 +143,16 @@ class ContextAPI(object): try: with open(filepath, "r") as f: data = json.load(f) - names.append(data["Name"]) + name = data["Name"] + if name == "default": + raise ValueError('"default" is a reserved context name') + names.append(name) except Exception as e: raise_from(errors.ContextException( "Failed to load metafile {filepath}: {e}".format(filepath=filepath, e=e), ), e) - contexts = [cls.DEFAULT_CONTEXT] + contexts = [cls.get_default_context()] for name in names: contexts.append(Context.load_context(name)) return contexts @@ -193,7 +211,7 @@ class ContextAPI(object): @classmethod def inspect_context(cls, name="default"): - """Remove a context. Similar to the ``docker context inspect`` command. + """Inspect a context. Similar to the ``docker context inspect`` command. Args: name (str): The name of the context @@ -213,7 +231,7 @@ class ContextAPI(object): if not name: raise errors.MissingContextParameter("name") if name == "default": - return cls.DEFAULT_CONTEXT() + return cls.get_default_context()() ctx = Context.load_context(name) if not ctx: raise errors.ContextNotFound(name) diff --git a/plugins/module_utils/_api/context/config.py b/plugins/module_utils/_api/context/config.py index df0e2651..f339d2b1 100644 --- a/plugins/module_utils/_api/context/config.py +++ b/plugins/module_utils/_api/context/config.py @@ -23,6 +23,10 @@ METAFILE = "meta.json" def get_current_context_name(): name = "default" + if os.environ.get('DOCKER_HOST'): + return name + if os.environ.get('DOCKER_CONTEXT'): + return os.environ['DOCKER_CONTEXT'] docker_cfg_path = find_config_file() if docker_cfg_path: try: diff --git a/plugins/module_utils/_api/context/context.py b/plugins/module_utils/_api/context/context.py index 3df29faa..3111ee9f 100644 --- a/plugins/module_utils/_api/context/context.py +++ b/plugins/module_utils/_api/context/context.py @@ -27,11 +27,14 @@ from .config import ( ) +IN_MEMORY = "IN MEMORY" + + class Context(object): """A context.""" def __init__(self, name, orchestrator=None, host=None, endpoints=None, - tls=False): + tls=False, description=None): if not name: raise Exception("Name not provided") self.name = name @@ -39,8 +42,9 @@ class Context(object): self.orchestrator = orchestrator self.endpoints = {} self.tls_cfg = {} - self.meta_path = "IN MEMORY" - self.tls_path = "IN MEMORY" + self.meta_path = IN_MEMORY + self.tls_path = IN_MEMORY + self.description = description if not endpoints: # set default docker endpoint if no endpoint is set @@ -96,7 +100,8 @@ class Context(object): instance = cls( meta["Name"], orchestrator=meta["Metadata"].get("StackOrchestrator", None), - endpoints=meta.get("Endpoints", None)) + endpoints=meta.get("Endpoints", None), + description=meta.get('Description')) instance.context_type = meta["Metadata"].get("Type", None) instance._load_certs() instance.meta_path = get_meta_dir(name) diff --git a/plugins/module_utils/_api/utils/utils.py b/plugins/module_utils/_api/utils/utils.py index 81b28a50..7c1b4f89 100644 --- a/plugins/module_utils/_api/utils/utils.py +++ b/plugins/module_utils/_api/utils/utils.py @@ -303,12 +303,7 @@ def parse_host(addr, is_win32=False, tls=False): if proto in ('tcp', 'ssh'): port = parsed_url.port or 0 if port <= 0: - if proto != 'ssh': - raise errors.DockerException( - 'Invalid bind address format: port is required:' - ' {0}'.format(addr) - ) - port = 22 + port = 22 if proto == 'ssh' else (2375 if tls else 2376) netloc = '{0}:{1}'.format(parsed_url.netloc, port) if not parsed_url.hostname: diff --git a/tests/unit/plugins/module_utils/_api/utils/test_utils.py b/tests/unit/plugins/module_utils/_api/utils/test_utils.py index 8e4d72d0..e724b079 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_utils.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_utils.py @@ -255,7 +255,7 @@ class ParseEnvFileTest(unittest.TestCase): class ParseHostTest(unittest.TestCase): def test_parse_host(self): invalid_hosts = [ - '0.0.0.0', + 'foo://0.0.0.0', 'tcp://', 'udp://127.0.0.1', 'udp://127.0.0.1:2375',