Adjust more to behavior of Docker CLI.

This commit is contained in:
Felix Fontein 2025-02-06 13:16:23 +01:00
parent 4e76d15dd4
commit e1baff04eb
5 changed files with 39 additions and 17 deletions

View File

@ -26,12 +26,27 @@ from .config import (
from .context import Context 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): class ContextAPI(object):
"""Context API. """Context API.
Contains methods for context management: Contains methods for context management:
create, list, remove, get, inspect. 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 @classmethod
def create_context( def create_context(
@ -108,7 +123,7 @@ class ContextAPI(object):
if not name: if not name:
name = get_current_context_name() name = get_current_context_name()
if name == "default": if name == "default":
return cls.DEFAULT_CONTEXT return cls.get_default_context()
return Context.load_context(name) return Context.load_context(name)
@classmethod @classmethod
@ -128,13 +143,16 @@ class ContextAPI(object):
try: try:
with open(filepath, "r") as f: with open(filepath, "r") as f:
data = json.load(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: except Exception as e:
raise_from(errors.ContextException( raise_from(errors.ContextException(
"Failed to load metafile {filepath}: {e}".format(filepath=filepath, e=e), "Failed to load metafile {filepath}: {e}".format(filepath=filepath, e=e),
), e) ), e)
contexts = [cls.DEFAULT_CONTEXT] contexts = [cls.get_default_context()]
for name in names: for name in names:
contexts.append(Context.load_context(name)) contexts.append(Context.load_context(name))
return contexts return contexts
@ -193,7 +211,7 @@ class ContextAPI(object):
@classmethod @classmethod
def inspect_context(cls, name="default"): 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: Args:
name (str): The name of the context name (str): The name of the context
@ -213,7 +231,7 @@ class ContextAPI(object):
if not name: if not name:
raise errors.MissingContextParameter("name") raise errors.MissingContextParameter("name")
if name == "default": if name == "default":
return cls.DEFAULT_CONTEXT() return cls.get_default_context()()
ctx = Context.load_context(name) ctx = Context.load_context(name)
if not ctx: if not ctx:
raise errors.ContextNotFound(name) raise errors.ContextNotFound(name)

View File

@ -23,6 +23,10 @@ METAFILE = "meta.json"
def get_current_context_name(): def get_current_context_name():
name = "default" 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() docker_cfg_path = find_config_file()
if docker_cfg_path: if docker_cfg_path:
try: try:

View File

@ -27,11 +27,14 @@ from .config import (
) )
IN_MEMORY = "IN MEMORY"
class Context(object): class Context(object):
"""A context.""" """A context."""
def __init__(self, name, orchestrator=None, host=None, endpoints=None, def __init__(self, name, orchestrator=None, host=None, endpoints=None,
tls=False): tls=False, description=None):
if not name: if not name:
raise Exception("Name not provided") raise Exception("Name not provided")
self.name = name self.name = name
@ -39,8 +42,9 @@ class Context(object):
self.orchestrator = orchestrator self.orchestrator = orchestrator
self.endpoints = {} self.endpoints = {}
self.tls_cfg = {} self.tls_cfg = {}
self.meta_path = "IN MEMORY" self.meta_path = IN_MEMORY
self.tls_path = "IN MEMORY" self.tls_path = IN_MEMORY
self.description = description
if not endpoints: if not endpoints:
# set default docker endpoint if no endpoint is set # set default docker endpoint if no endpoint is set
@ -96,7 +100,8 @@ class Context(object):
instance = cls( instance = cls(
meta["Name"], meta["Name"],
orchestrator=meta["Metadata"].get("StackOrchestrator", None), 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.context_type = meta["Metadata"].get("Type", None)
instance._load_certs() instance._load_certs()
instance.meta_path = get_meta_dir(name) instance.meta_path = get_meta_dir(name)

View File

@ -303,12 +303,7 @@ def parse_host(addr, is_win32=False, tls=False):
if proto in ('tcp', 'ssh'): if proto in ('tcp', 'ssh'):
port = parsed_url.port or 0 port = parsed_url.port or 0
if port <= 0: if port <= 0:
if proto != 'ssh': port = 22 if proto == 'ssh' else (2375 if tls else 2376)
raise errors.DockerException(
'Invalid bind address format: port is required:'
' {0}'.format(addr)
)
port = 22
netloc = '{0}:{1}'.format(parsed_url.netloc, port) netloc = '{0}:{1}'.format(parsed_url.netloc, port)
if not parsed_url.hostname: if not parsed_url.hostname:

View File

@ -255,7 +255,7 @@ class ParseEnvFileTest(unittest.TestCase):
class ParseHostTest(unittest.TestCase): class ParseHostTest(unittest.TestCase):
def test_parse_host(self): def test_parse_host(self):
invalid_hosts = [ invalid_hosts = [
'0.0.0.0', 'foo://0.0.0.0',
'tcp://', 'tcp://',
'udp://127.0.0.1', 'udp://127.0.0.1',
'udp://127.0.0.1:2375', 'udp://127.0.0.1:2375',