mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-07-29 11:55:04 +00:00
Reformat code with black and isort.
This commit is contained in:
@@ -16,11 +16,11 @@ import re
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
from . import fnmatch
|
||||
from ..constants import IS_WINDOWS_PLATFORM, WINDOWS_LONGPATH_PREFIX
|
||||
from . import fnmatch
|
||||
|
||||
|
||||
_SEP = re.compile('/|\\\\') if IS_WINDOWS_PLATFORM else re.compile('/')
|
||||
_SEP = re.compile("/|\\\\") if IS_WINDOWS_PLATFORM else re.compile("/")
|
||||
|
||||
|
||||
def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
|
||||
@@ -29,16 +29,19 @@ def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
|
||||
dockerfile = dockerfile or (None, None)
|
||||
extra_files = []
|
||||
if dockerfile[1] is not None:
|
||||
dockerignore_contents = '\n'.join(
|
||||
(exclude or ['.dockerignore']) + [dockerfile[0]]
|
||||
dockerignore_contents = "\n".join(
|
||||
(exclude or [".dockerignore"]) + [dockerfile[0]]
|
||||
)
|
||||
extra_files = [
|
||||
('.dockerignore', dockerignore_contents),
|
||||
(".dockerignore", dockerignore_contents),
|
||||
dockerfile,
|
||||
]
|
||||
return create_archive(
|
||||
files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile[0])),
|
||||
root=root, fileobj=fileobj, gzip=gzip, extra_files=extra_files
|
||||
root=root,
|
||||
fileobj=fileobj,
|
||||
gzip=gzip,
|
||||
extra_files=extra_files,
|
||||
)
|
||||
|
||||
|
||||
@@ -52,9 +55,9 @@ def exclude_paths(root, patterns, dockerfile=None):
|
||||
"""
|
||||
|
||||
if dockerfile is None:
|
||||
dockerfile = 'Dockerfile'
|
||||
dockerfile = "Dockerfile"
|
||||
|
||||
patterns.append('!' + dockerfile)
|
||||
patterns.append("!" + dockerfile)
|
||||
pm = PatternMatcher(patterns)
|
||||
return set(pm.walk(root))
|
||||
|
||||
@@ -64,19 +67,16 @@ def build_file_list(root):
|
||||
for dirname, dirnames, fnames in os.walk(root):
|
||||
for filename in fnames + dirnames:
|
||||
longpath = os.path.join(dirname, filename)
|
||||
files.append(
|
||||
longpath.replace(root, '', 1).lstrip('/')
|
||||
)
|
||||
files.append(longpath.replace(root, "", 1).lstrip("/"))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def create_archive(root, files=None, fileobj=None, gzip=False,
|
||||
extra_files=None):
|
||||
def create_archive(root, files=None, fileobj=None, gzip=False, extra_files=None):
|
||||
extra_files = extra_files or []
|
||||
if not fileobj:
|
||||
fileobj = tempfile.NamedTemporaryFile()
|
||||
t = tarfile.open(mode='w:gz' if gzip else 'w', fileobj=fileobj)
|
||||
t = tarfile.open(mode="w:gz" if gzip else "w", fileobj=fileobj)
|
||||
if files is None:
|
||||
files = build_file_list(root)
|
||||
extra_names = set(e[0] for e in extra_files)
|
||||
@@ -103,19 +103,17 @@ def create_archive(root, files=None, fileobj=None, gzip=False,
|
||||
|
||||
if i.isfile():
|
||||
try:
|
||||
with open(full_path, 'rb') as f:
|
||||
with open(full_path, "rb") as f:
|
||||
t.addfile(i, f)
|
||||
except IOError:
|
||||
raise IOError(
|
||||
f'Can not read file in context: {full_path}'
|
||||
)
|
||||
raise IOError(f"Can not read file in context: {full_path}")
|
||||
else:
|
||||
# Directories, FIFOs, symlinks... do not need to be read.
|
||||
t.addfile(i, None)
|
||||
|
||||
for name, contents in extra_files:
|
||||
info = tarfile.TarInfo(name)
|
||||
contents_encoded = contents.encode('utf-8')
|
||||
contents_encoded = contents.encode("utf-8")
|
||||
info.size = len(contents_encoded)
|
||||
t.addfile(info, io.BytesIO(contents_encoded))
|
||||
|
||||
@@ -126,15 +124,15 @@ def create_archive(root, files=None, fileobj=None, gzip=False,
|
||||
|
||||
def mkbuildcontext(dockerfile):
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
t = tarfile.open(mode='w', fileobj=f)
|
||||
t = tarfile.open(mode="w", fileobj=f)
|
||||
if isinstance(dockerfile, io.StringIO):
|
||||
raise TypeError('Please use io.BytesIO to create in-memory Dockerfiles')
|
||||
raise TypeError("Please use io.BytesIO to create in-memory Dockerfiles")
|
||||
elif isinstance(dockerfile, io.BytesIO):
|
||||
dfinfo = tarfile.TarInfo('Dockerfile')
|
||||
dfinfo = tarfile.TarInfo("Dockerfile")
|
||||
dfinfo.size = len(dockerfile.getvalue())
|
||||
dockerfile.seek(0)
|
||||
else:
|
||||
dfinfo = t.gettarinfo(fileobj=dockerfile, arcname='Dockerfile')
|
||||
dfinfo = t.gettarinfo(fileobj=dockerfile, arcname="Dockerfile")
|
||||
t.addfile(dfinfo, dockerfile)
|
||||
t.close()
|
||||
f.seek(0)
|
||||
@@ -142,12 +140,12 @@ def mkbuildcontext(dockerfile):
|
||||
|
||||
|
||||
def split_path(p):
|
||||
return [pt for pt in re.split(_SEP, p) if pt and pt != '.']
|
||||
return [pt for pt in re.split(_SEP, p) if pt and pt != "."]
|
||||
|
||||
|
||||
def normalize_slashes(p):
|
||||
if IS_WINDOWS_PLATFORM:
|
||||
return '/'.join(split_path(p))
|
||||
return "/".join(split_path(p))
|
||||
return p
|
||||
|
||||
|
||||
@@ -160,10 +158,8 @@ def walk(root, patterns, default=True):
|
||||
# https://github.com/moby/moby/blob/master/pkg/fileutils/fileutils.go
|
||||
class PatternMatcher(object):
|
||||
def __init__(self, patterns):
|
||||
self.patterns = list(filter(
|
||||
lambda p: p.dirs, [Pattern(p) for p in patterns]
|
||||
))
|
||||
self.patterns.append(Pattern('!.dockerignore'))
|
||||
self.patterns = list(filter(lambda p: p.dirs, [Pattern(p) for p in patterns]))
|
||||
self.patterns.append(Pattern("!.dockerignore"))
|
||||
|
||||
def matches(self, filepath):
|
||||
matched = False
|
||||
@@ -173,10 +169,10 @@ class PatternMatcher(object):
|
||||
for pattern in self.patterns:
|
||||
negative = pattern.exclusion
|
||||
match = pattern.match(filepath)
|
||||
if not match and parent_path != '':
|
||||
if not match and parent_path != "":
|
||||
if len(pattern.dirs) <= len(parent_path_dirs):
|
||||
match = pattern.match(
|
||||
os.path.sep.join(parent_path_dirs[:len(pattern.dirs)])
|
||||
os.path.sep.join(parent_path_dirs[: len(pattern.dirs)])
|
||||
)
|
||||
|
||||
if match:
|
||||
@@ -187,10 +183,8 @@ class PatternMatcher(object):
|
||||
def walk(self, root):
|
||||
def rec_walk(current_dir):
|
||||
for f in os.listdir(current_dir):
|
||||
fpath = os.path.join(
|
||||
os.path.relpath(current_dir, root), f
|
||||
)
|
||||
if fpath.startswith('.' + os.path.sep):
|
||||
fpath = os.path.join(os.path.relpath(current_dir, root), f)
|
||||
if fpath.startswith("." + os.path.sep):
|
||||
fpath = fpath[2:]
|
||||
match = self.matches(fpath)
|
||||
if not match:
|
||||
@@ -210,8 +204,7 @@ class PatternMatcher(object):
|
||||
for pat in self.patterns:
|
||||
if not pat.exclusion:
|
||||
continue
|
||||
if pat.cleaned_pattern.startswith(
|
||||
normalize_slashes(fpath)):
|
||||
if pat.cleaned_pattern.startswith(normalize_slashes(fpath)):
|
||||
skip = False
|
||||
break
|
||||
if skip:
|
||||
@@ -224,12 +217,12 @@ class PatternMatcher(object):
|
||||
class Pattern(object):
|
||||
def __init__(self, pattern_str):
|
||||
self.exclusion = False
|
||||
if pattern_str.startswith('!'):
|
||||
if pattern_str.startswith("!"):
|
||||
self.exclusion = True
|
||||
pattern_str = pattern_str[1:]
|
||||
|
||||
self.dirs = self.normalize(pattern_str)
|
||||
self.cleaned_pattern = '/'.join(self.dirs)
|
||||
self.cleaned_pattern = "/".join(self.dirs)
|
||||
|
||||
@classmethod
|
||||
def normalize(cls, p):
|
||||
@@ -249,7 +242,7 @@ class Pattern(object):
|
||||
i = 0
|
||||
split = split_path(p)
|
||||
while i < len(split):
|
||||
if split[i] == '..':
|
||||
if split[i] == "..":
|
||||
del split[i]
|
||||
if i > 0:
|
||||
del split[i - 1]
|
||||
@@ -269,17 +262,14 @@ def process_dockerfile(dockerfile, path):
|
||||
abs_dockerfile = dockerfile
|
||||
if not os.path.isabs(dockerfile):
|
||||
abs_dockerfile = os.path.join(path, dockerfile)
|
||||
if IS_WINDOWS_PLATFORM and path.startswith(
|
||||
WINDOWS_LONGPATH_PREFIX):
|
||||
abs_dockerfile = f'{WINDOWS_LONGPATH_PREFIX}{os.path.normpath(abs_dockerfile[len(WINDOWS_LONGPATH_PREFIX):])}'
|
||||
if (os.path.splitdrive(path)[0] != os.path.splitdrive(abs_dockerfile)[0] or
|
||||
os.path.relpath(abs_dockerfile, path).startswith('..')):
|
||||
if IS_WINDOWS_PLATFORM and path.startswith(WINDOWS_LONGPATH_PREFIX):
|
||||
abs_dockerfile = f"{WINDOWS_LONGPATH_PREFIX}{os.path.normpath(abs_dockerfile[len(WINDOWS_LONGPATH_PREFIX):])}"
|
||||
if os.path.splitdrive(path)[0] != os.path.splitdrive(abs_dockerfile)[
|
||||
0
|
||||
] or os.path.relpath(abs_dockerfile, path).startswith(".."):
|
||||
# Dockerfile not in context - read data to insert into tar later
|
||||
with open(abs_dockerfile) as df:
|
||||
return (
|
||||
f'.dockerfile.{random.getrandbits(160):x}',
|
||||
df.read()
|
||||
)
|
||||
return (f".dockerfile.{random.getrandbits(160):x}", df.read())
|
||||
|
||||
# Dockerfile is inside the context - return path relative to context root
|
||||
if dockerfile == abs_dockerfile:
|
||||
|
||||
@@ -15,8 +15,9 @@ import os
|
||||
|
||||
from ..constants import IS_WINDOWS_PLATFORM
|
||||
|
||||
DOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')
|
||||
LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
|
||||
|
||||
DOCKER_CONFIG_FILENAME = os.path.join(".docker", "config.json")
|
||||
LEGACY_DOCKER_CONFIG_FILENAME = ".dockercfg"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -27,12 +28,17 @@ def get_default_config_file():
|
||||
|
||||
def find_config_file(config_path=None):
|
||||
homedir = home_dir()
|
||||
paths = list(filter(None, [
|
||||
config_path, # 1
|
||||
config_path_from_environment(), # 2
|
||||
os.path.join(homedir, DOCKER_CONFIG_FILENAME), # 3
|
||||
os.path.join(homedir, LEGACY_DOCKER_CONFIG_FILENAME), # 4
|
||||
]))
|
||||
paths = list(
|
||||
filter(
|
||||
None,
|
||||
[
|
||||
config_path, # 1
|
||||
config_path_from_environment(), # 2
|
||||
os.path.join(homedir, DOCKER_CONFIG_FILENAME), # 3
|
||||
os.path.join(homedir, LEGACY_DOCKER_CONFIG_FILENAME), # 4
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
log.debug("Trying paths: %s", repr(paths))
|
||||
|
||||
@@ -47,7 +53,7 @@ def find_config_file(config_path=None):
|
||||
|
||||
|
||||
def config_path_from_environment():
|
||||
config_dir = os.environ.get('DOCKER_CONFIG')
|
||||
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))
|
||||
@@ -59,9 +65,9 @@ def home_dir():
|
||||
client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX.
|
||||
"""
|
||||
if IS_WINDOWS_PLATFORM:
|
||||
return os.environ.get('USERPROFILE', '')
|
||||
return os.environ.get("USERPROFILE", "")
|
||||
else:
|
||||
return os.path.expanduser('~')
|
||||
return os.path.expanduser("~")
|
||||
|
||||
|
||||
def load_general_config(config_path=None):
|
||||
|
||||
@@ -22,13 +22,13 @@ def check_resource(resource_name):
|
||||
if resource_id is None and kwargs.get(resource_name):
|
||||
resource_id = kwargs.pop(resource_name)
|
||||
if isinstance(resource_id, dict):
|
||||
resource_id = resource_id.get('Id', resource_id.get('ID'))
|
||||
resource_id = resource_id.get("Id", resource_id.get("ID"))
|
||||
if not resource_id:
|
||||
raise errors.NullResource(
|
||||
'Resource ID was not provided'
|
||||
)
|
||||
raise errors.NullResource("Resource ID was not provided")
|
||||
return f(self, resource_id, *args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -38,19 +38,22 @@ def minimum_version(version):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if utils.version_lt(self._version, version):
|
||||
raise errors.InvalidVersion(
|
||||
f'{f.__name__} is not available for version < {version}'
|
||||
f"{f.__name__} is not available for version < {version}"
|
||||
)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def update_headers(f):
|
||||
def inner(self, *args, **kwargs):
|
||||
if 'HttpHeaders' in self._general_configs:
|
||||
if not kwargs.get('headers'):
|
||||
kwargs['headers'] = self._general_configs['HttpHeaders']
|
||||
if "HttpHeaders" in self._general_configs:
|
||||
if not kwargs.get("headers"):
|
||||
kwargs["headers"] = self._general_configs["HttpHeaders"]
|
||||
else:
|
||||
kwargs['headers'].update(self._general_configs['HttpHeaders'])
|
||||
kwargs["headers"].update(self._general_configs["HttpHeaders"])
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return inner
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
"""Filename matching with shell patterns.
|
||||
|
||||
fnmatch(FILENAME, PATTERN) matches according to the local convention.
|
||||
@@ -23,6 +24,7 @@ corresponding to PATTERN. (It does not compile it.)
|
||||
|
||||
import re
|
||||
|
||||
|
||||
__all__ = ["fnmatch", "fnmatchcase", "translate"]
|
||||
|
||||
_cache = {}
|
||||
@@ -77,50 +79,50 @@ def translate(pat):
|
||||
There is no way to quote meta-characters.
|
||||
"""
|
||||
i, n = 0, len(pat)
|
||||
res = '^'
|
||||
res = "^"
|
||||
while i < n:
|
||||
c = pat[i]
|
||||
i = i + 1
|
||||
if c == '*':
|
||||
if i < n and pat[i] == '*':
|
||||
if c == "*":
|
||||
if i < n and pat[i] == "*":
|
||||
# is some flavor of "**"
|
||||
i = i + 1
|
||||
# Treat **/ as ** so eat the "/"
|
||||
if i < n and pat[i] == '/':
|
||||
if i < n and pat[i] == "/":
|
||||
i = i + 1
|
||||
if i >= n:
|
||||
# is "**EOF" - to align with .gitignore just accept all
|
||||
res = res + '.*'
|
||||
res = res + ".*"
|
||||
else:
|
||||
# is "**"
|
||||
# Note that this allows for any # of /'s (even 0) because
|
||||
# the .* will eat everything, even /'s
|
||||
res = res + '(.*/)?'
|
||||
res = res + "(.*/)?"
|
||||
else:
|
||||
# is "*" so map it to anything but "/"
|
||||
res = res + '[^/]*'
|
||||
elif c == '?':
|
||||
res = res + "[^/]*"
|
||||
elif c == "?":
|
||||
# "?" is any char except "/"
|
||||
res = res + '[^/]'
|
||||
elif c == '[':
|
||||
res = res + "[^/]"
|
||||
elif c == "[":
|
||||
j = i
|
||||
if j < n and pat[j] == '!':
|
||||
if j < n and pat[j] == "!":
|
||||
j = j + 1
|
||||
if j < n and pat[j] == ']':
|
||||
if j < n and pat[j] == "]":
|
||||
j = j + 1
|
||||
while j < n and pat[j] != ']':
|
||||
while j < n and pat[j] != "]":
|
||||
j = j + 1
|
||||
if j >= n:
|
||||
res = res + '\\['
|
||||
res = res + "\\["
|
||||
else:
|
||||
stuff = pat[i:j].replace('\\', '\\\\')
|
||||
stuff = pat[i:j].replace("\\", "\\\\")
|
||||
i = j + 1
|
||||
if stuff[0] == '!':
|
||||
stuff = '^' + stuff[1:]
|
||||
elif stuff[0] == '^':
|
||||
stuff = '\\' + stuff
|
||||
res = f'{res}[{stuff}]'
|
||||
if stuff[0] == "!":
|
||||
stuff = "^" + stuff[1:]
|
||||
elif stuff[0] == "^":
|
||||
stuff = "\\" + stuff
|
||||
res = f"{res}[{stuff}]"
|
||||
else:
|
||||
res = res + re.escape(c)
|
||||
|
||||
return res + '$'
|
||||
return res + "$"
|
||||
|
||||
@@ -27,7 +27,7 @@ def stream_as_text(stream):
|
||||
"""
|
||||
for data in stream:
|
||||
if not isinstance(data, str):
|
||||
data = data.decode('utf-8', 'replace')
|
||||
data = data.decode("utf-8", "replace")
|
||||
yield data
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ def json_splitter(buffer):
|
||||
buffer = buffer.strip()
|
||||
try:
|
||||
obj, index = json_decoder.raw_decode(buffer)
|
||||
rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end():]
|
||||
rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end() :]
|
||||
return obj, rest
|
||||
except ValueError:
|
||||
return None
|
||||
@@ -52,11 +52,11 @@ def json_stream(stream):
|
||||
return split_buffer(stream, json_splitter, json_decoder.decode)
|
||||
|
||||
|
||||
def line_splitter(buffer, separator='\n'):
|
||||
def line_splitter(buffer, separator="\n"):
|
||||
index = buffer.find(str(separator))
|
||||
if index == -1:
|
||||
return None
|
||||
return buffer[:index + 1], buffer[index + 1:]
|
||||
return buffer[: index + 1], buffer[index + 1 :]
|
||||
|
||||
|
||||
def split_buffer(stream, splitter=None, decoder=lambda a: a):
|
||||
@@ -67,7 +67,7 @@ def split_buffer(stream, splitter=None, decoder=lambda a: a):
|
||||
of the input.
|
||||
"""
|
||||
splitter = splitter or line_splitter
|
||||
buffered = ''
|
||||
buffered = ""
|
||||
|
||||
for data in stream_as_text(stream):
|
||||
buffered += data
|
||||
|
||||
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
|
||||
PORT_SPEC = re.compile(
|
||||
"^" # Match full string
|
||||
"(" # External part
|
||||
@@ -49,23 +50,25 @@ def build_port_bindings(ports):
|
||||
|
||||
|
||||
def _raise_invalid_port(port):
|
||||
raise ValueError(f'Invalid port "{port}", should be '
|
||||
'[[remote_ip:]remote_port[-remote_port]:]'
|
||||
'port[/protocol]')
|
||||
raise ValueError(
|
||||
f'Invalid port "{port}", should be '
|
||||
"[[remote_ip:]remote_port[-remote_port]:]"
|
||||
"port[/protocol]"
|
||||
)
|
||||
|
||||
|
||||
def port_range(start, end, proto, randomly_available_port=False):
|
||||
if not start:
|
||||
return start
|
||||
if not end:
|
||||
return [f'{start}{proto}']
|
||||
return [f"{start}{proto}"]
|
||||
if randomly_available_port:
|
||||
return [f'{start}-{end}{proto}']
|
||||
return [f'{port}{proto}' for port in range(int(start), int(end) + 1)]
|
||||
return [f"{start}-{end}{proto}"]
|
||||
return [f"{port}{proto}" for port in range(int(start), int(end) + 1)]
|
||||
|
||||
|
||||
def split_port(port):
|
||||
if hasattr(port, 'legacy_repr'):
|
||||
if hasattr(port, "legacy_repr"):
|
||||
# This is the worst hack, but it prevents a bug in Compose 1.14.0
|
||||
# https://github.com/docker/docker-py/issues/1668
|
||||
# TODO: remove once fixed in Compose stable
|
||||
@@ -76,19 +79,18 @@ def split_port(port):
|
||||
_raise_invalid_port(port)
|
||||
parts = match.groupdict()
|
||||
|
||||
host = parts['host']
|
||||
proto = parts['proto'] or ''
|
||||
internal = port_range(parts['int'], parts['int_end'], proto)
|
||||
external = port_range(
|
||||
parts['ext'], parts['ext_end'], '', len(internal) == 1)
|
||||
host = parts["host"]
|
||||
proto = parts["proto"] or ""
|
||||
internal = port_range(parts["int"], parts["int_end"], proto)
|
||||
external = port_range(parts["ext"], parts["ext_end"], "", len(internal) == 1)
|
||||
|
||||
if host is None:
|
||||
if external is not None and len(internal) != len(external):
|
||||
raise ValueError('Port ranges don\'t match in length')
|
||||
raise ValueError("Port ranges don't match in length")
|
||||
return internal, external
|
||||
else:
|
||||
if not external:
|
||||
external = [None] * len(internal)
|
||||
elif len(internal) != len(external):
|
||||
raise ValueError('Port ranges don\'t match in length')
|
||||
raise ValueError("Port ranges don't match in length")
|
||||
return internal, [(host, ext_port) for ext_port in external]
|
||||
|
||||
@@ -13,62 +13,63 @@ from .utils import format_environment
|
||||
|
||||
|
||||
class ProxyConfig(dict):
|
||||
'''
|
||||
"""
|
||||
Hold the client's proxy configuration
|
||||
'''
|
||||
"""
|
||||
|
||||
@property
|
||||
def http(self):
|
||||
return self.get('http')
|
||||
return self.get("http")
|
||||
|
||||
@property
|
||||
def https(self):
|
||||
return self.get('https')
|
||||
return self.get("https")
|
||||
|
||||
@property
|
||||
def ftp(self):
|
||||
return self.get('ftp')
|
||||
return self.get("ftp")
|
||||
|
||||
@property
|
||||
def no_proxy(self):
|
||||
return self.get('no_proxy')
|
||||
return self.get("no_proxy")
|
||||
|
||||
@staticmethod
|
||||
def from_dict(config):
|
||||
'''
|
||||
"""
|
||||
Instantiate a new ProxyConfig from a dictionary that represents a
|
||||
client configuration, as described in `the documentation`_.
|
||||
|
||||
.. _the documentation:
|
||||
https://docs.docker.com/network/proxy/#configure-the-docker-client
|
||||
'''
|
||||
"""
|
||||
return ProxyConfig(
|
||||
http=config.get('httpProxy'),
|
||||
https=config.get('httpsProxy'),
|
||||
ftp=config.get('ftpProxy'),
|
||||
no_proxy=config.get('noProxy'),
|
||||
http=config.get("httpProxy"),
|
||||
https=config.get("httpsProxy"),
|
||||
ftp=config.get("ftpProxy"),
|
||||
no_proxy=config.get("noProxy"),
|
||||
)
|
||||
|
||||
def get_environment(self):
|
||||
'''
|
||||
"""
|
||||
Return a dictionary representing the environment variables used to
|
||||
set the proxy settings.
|
||||
'''
|
||||
"""
|
||||
env = {}
|
||||
if self.http:
|
||||
env['http_proxy'] = env['HTTP_PROXY'] = self.http
|
||||
env["http_proxy"] = env["HTTP_PROXY"] = self.http
|
||||
if self.https:
|
||||
env['https_proxy'] = env['HTTPS_PROXY'] = self.https
|
||||
env["https_proxy"] = env["HTTPS_PROXY"] = self.https
|
||||
if self.ftp:
|
||||
env['ftp_proxy'] = env['FTP_PROXY'] = self.ftp
|
||||
env["ftp_proxy"] = env["FTP_PROXY"] = self.ftp
|
||||
if self.no_proxy:
|
||||
env['no_proxy'] = env['NO_PROXY'] = self.no_proxy
|
||||
env["no_proxy"] = env["NO_PROXY"] = self.no_proxy
|
||||
return env
|
||||
|
||||
def inject_proxy_environment(self, environment):
|
||||
'''
|
||||
"""
|
||||
Given a list of strings representing environment variables, prepend the
|
||||
environment variables corresponding to the proxy settings.
|
||||
'''
|
||||
"""
|
||||
if not self:
|
||||
return environment
|
||||
|
||||
@@ -80,4 +81,4 @@ class ProxyConfig(dict):
|
||||
return proxy_env + environment
|
||||
|
||||
def __str__(self):
|
||||
return f'ProxyConfig(http={self.http}, https={self.https}, ftp={self.ftp}, no_proxy={self.no_proxy})'
|
||||
return f"ProxyConfig(http={self.http}, https={self.https}, ftp={self.ftp}, no_proxy={self.no_proxy})"
|
||||
|
||||
@@ -48,22 +48,24 @@ def read(socket, n=4096):
|
||||
poll.poll()
|
||||
|
||||
try:
|
||||
if hasattr(socket, 'recv'):
|
||||
if hasattr(socket, "recv"):
|
||||
return socket.recv(n)
|
||||
if isinstance(socket, getattr(pysocket, 'SocketIO')):
|
||||
if isinstance(socket, getattr(pysocket, "SocketIO")):
|
||||
return socket.read(n)
|
||||
return os.read(socket.fileno(), n)
|
||||
except EnvironmentError as e:
|
||||
if e.errno not in recoverable_errors:
|
||||
raise
|
||||
except Exception as e:
|
||||
is_pipe_ended = (isinstance(socket, NpipeSocket) and
|
||||
len(e.args) > 0 and
|
||||
e.args[0] == NPIPE_ENDED)
|
||||
is_pipe_ended = (
|
||||
isinstance(socket, NpipeSocket)
|
||||
and len(e.args) > 0
|
||||
and e.args[0] == NPIPE_ENDED
|
||||
)
|
||||
if is_pipe_ended:
|
||||
# npipes do not support duplex sockets, so we interpret
|
||||
# a PIPE_ENDED error as a close operation (0-length read).
|
||||
return ''
|
||||
return ""
|
||||
raise
|
||||
|
||||
|
||||
@@ -72,7 +74,7 @@ def read_exactly(socket, n):
|
||||
Reads exactly n bytes from socket
|
||||
Raises SocketError if there is not enough data
|
||||
"""
|
||||
data = b''
|
||||
data = b""
|
||||
while len(data) < n:
|
||||
next_data = read(socket, n - len(data))
|
||||
if not next_data:
|
||||
@@ -93,7 +95,7 @@ def next_frame_header(socket):
|
||||
except SocketError:
|
||||
return (-1, -1)
|
||||
|
||||
stream, actual = struct.unpack('>BxxxL', data)
|
||||
stream, actual = struct.unpack(">BxxxL", data)
|
||||
return (stream, actual)
|
||||
|
||||
|
||||
@@ -160,7 +162,7 @@ def consume_socket_output(frames, demux=False):
|
||||
if demux is False:
|
||||
# If the streams are multiplexed, the generator returns strings, that
|
||||
# we just need to concatenate.
|
||||
return b''.join(frames)
|
||||
return b"".join(frames)
|
||||
|
||||
# If the streams are demultiplexed, the generator yields tuples
|
||||
# (stdout, stderr)
|
||||
@@ -169,7 +171,7 @@ def consume_socket_output(frames, demux=False):
|
||||
# It is guaranteed that for each frame, one and only one stream
|
||||
# is not None.
|
||||
if frame == (None, None):
|
||||
raise AssertionError(f'frame must be (None, None), but got {frame}')
|
||||
raise AssertionError(f"frame must be (None, None), but got {frame}")
|
||||
if frame[0] is not None:
|
||||
if out[0] is None:
|
||||
out[0] = frame[0]
|
||||
@@ -193,4 +195,4 @@ def demux_adaptor(stream_id, data):
|
||||
elif stream_id == STDERR:
|
||||
return (None, data)
|
||||
else:
|
||||
raise ValueError(f'{stream_id} is not a valid stream')
|
||||
raise ValueError(f"{stream_id} is not a valid stream")
|
||||
|
||||
@@ -16,40 +16,44 @@ import os
|
||||
import os.path
|
||||
import shlex
|
||||
import string
|
||||
from ansible_collections.community.docker.plugins.module_utils.version import StrictVersion
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.version import (
|
||||
StrictVersion,
|
||||
)
|
||||
|
||||
from .. import errors
|
||||
from ..constants import DEFAULT_HTTP_HOST
|
||||
from ..constants import DEFAULT_UNIX_SOCKET
|
||||
from ..constants import DEFAULT_NPIPE
|
||||
from ..constants import BYTE_UNITS
|
||||
from ..constants import (
|
||||
BYTE_UNITS,
|
||||
DEFAULT_HTTP_HOST,
|
||||
DEFAULT_NPIPE,
|
||||
DEFAULT_UNIX_SOCKET,
|
||||
)
|
||||
from ..tls import TLSConfig
|
||||
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
|
||||
URLComponents = collections.namedtuple(
|
||||
'URLComponents',
|
||||
'scheme netloc url params query fragment',
|
||||
"URLComponents",
|
||||
"scheme netloc url params query fragment",
|
||||
)
|
||||
|
||||
|
||||
def create_ipam_pool(*args, **kwargs):
|
||||
raise errors.DeprecatedMethod(
|
||||
'utils.create_ipam_pool has been removed. Please use a '
|
||||
'docker.types.IPAMPool object instead.'
|
||||
"utils.create_ipam_pool has been removed. Please use a "
|
||||
"docker.types.IPAMPool object instead."
|
||||
)
|
||||
|
||||
|
||||
def create_ipam_config(*args, **kwargs):
|
||||
raise errors.DeprecatedMethod(
|
||||
'utils.create_ipam_config has been removed. Please use a '
|
||||
'docker.types.IPAMConfig object instead.'
|
||||
"utils.create_ipam_config has been removed. Please use a "
|
||||
"docker.types.IPAMConfig object instead."
|
||||
)
|
||||
|
||||
|
||||
def decode_json_header(header):
|
||||
data = base64.b64decode(header).decode('utf-8')
|
||||
data = base64.b64decode(header).decode("utf-8")
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
@@ -84,29 +88,29 @@ def version_gte(v1, v2):
|
||||
|
||||
|
||||
def _convert_port_binding(binding):
|
||||
result = {'HostIp': '', 'HostPort': ''}
|
||||
result = {"HostIp": "", "HostPort": ""}
|
||||
if isinstance(binding, tuple):
|
||||
if len(binding) == 2:
|
||||
result['HostPort'] = binding[1]
|
||||
result['HostIp'] = binding[0]
|
||||
result["HostPort"] = binding[1]
|
||||
result["HostIp"] = binding[0]
|
||||
elif isinstance(binding[0], str):
|
||||
result['HostIp'] = binding[0]
|
||||
result["HostIp"] = binding[0]
|
||||
else:
|
||||
result['HostPort'] = binding[0]
|
||||
result["HostPort"] = binding[0]
|
||||
elif isinstance(binding, dict):
|
||||
if 'HostPort' in binding:
|
||||
result['HostPort'] = binding['HostPort']
|
||||
if 'HostIp' in binding:
|
||||
result['HostIp'] = binding['HostIp']
|
||||
if "HostPort" in binding:
|
||||
result["HostPort"] = binding["HostPort"]
|
||||
if "HostIp" in binding:
|
||||
result["HostIp"] = binding["HostIp"]
|
||||
else:
|
||||
raise ValueError(binding)
|
||||
else:
|
||||
result['HostPort'] = binding
|
||||
result["HostPort"] = binding
|
||||
|
||||
if result['HostPort'] is None:
|
||||
result['HostPort'] = ''
|
||||
if result["HostPort"] is None:
|
||||
result["HostPort"] = ""
|
||||
else:
|
||||
result['HostPort'] = str(result['HostPort'])
|
||||
result["HostPort"] = str(result["HostPort"])
|
||||
|
||||
return result
|
||||
|
||||
@@ -115,8 +119,8 @@ def convert_port_bindings(port_bindings):
|
||||
result = {}
|
||||
for k, v in port_bindings.items():
|
||||
key = str(k)
|
||||
if '/' not in key:
|
||||
key += '/tcp'
|
||||
if "/" not in key:
|
||||
key += "/tcp"
|
||||
if isinstance(v, list):
|
||||
result[key] = [_convert_port_binding(binding) for binding in v]
|
||||
else:
|
||||
@@ -131,46 +135,44 @@ def convert_volume_binds(binds):
|
||||
result = []
|
||||
for k, v in binds.items():
|
||||
if isinstance(k, bytes):
|
||||
k = k.decode('utf-8')
|
||||
k = k.decode("utf-8")
|
||||
|
||||
if isinstance(v, dict):
|
||||
if 'ro' in v and 'mode' in v:
|
||||
raise ValueError(
|
||||
f'Binding cannot contain both "ro" and "mode": {v!r}'
|
||||
)
|
||||
if "ro" in v and "mode" in v:
|
||||
raise ValueError(f'Binding cannot contain both "ro" and "mode": {v!r}')
|
||||
|
||||
bind = v['bind']
|
||||
bind = v["bind"]
|
||||
if isinstance(bind, bytes):
|
||||
bind = bind.decode('utf-8')
|
||||
bind = bind.decode("utf-8")
|
||||
|
||||
if 'ro' in v:
|
||||
mode = 'ro' if v['ro'] else 'rw'
|
||||
elif 'mode' in v:
|
||||
mode = v['mode']
|
||||
if "ro" in v:
|
||||
mode = "ro" if v["ro"] else "rw"
|
||||
elif "mode" in v:
|
||||
mode = v["mode"]
|
||||
else:
|
||||
mode = 'rw'
|
||||
mode = "rw"
|
||||
|
||||
# NOTE: this is only relevant for Linux hosts
|
||||
# (does not apply in Docker Desktop)
|
||||
propagation_modes = [
|
||||
'rshared',
|
||||
'shared',
|
||||
'rslave',
|
||||
'slave',
|
||||
'rprivate',
|
||||
'private',
|
||||
"rshared",
|
||||
"shared",
|
||||
"rslave",
|
||||
"slave",
|
||||
"rprivate",
|
||||
"private",
|
||||
]
|
||||
if 'propagation' in v and v['propagation'] in propagation_modes:
|
||||
if "propagation" in v and v["propagation"] in propagation_modes:
|
||||
if mode:
|
||||
mode = ','.join([mode, v['propagation']])
|
||||
mode = ",".join([mode, v["propagation"]])
|
||||
else:
|
||||
mode = v['propagation']
|
||||
mode = v["propagation"]
|
||||
|
||||
result.append(f'{k}:{bind}:{mode}')
|
||||
result.append(f"{k}:{bind}:{mode}")
|
||||
else:
|
||||
if isinstance(v, bytes):
|
||||
v = v.decode('utf-8')
|
||||
result.append(f'{k}:{v}:rw')
|
||||
v = v.decode("utf-8")
|
||||
result.append(f"{k}:{v}:rw")
|
||||
return result
|
||||
|
||||
|
||||
@@ -180,7 +182,7 @@ def convert_tmpfs_mounts(tmpfs):
|
||||
|
||||
if not isinstance(tmpfs, list):
|
||||
raise ValueError(
|
||||
f'Expected tmpfs value to be either a list or a dict, found: {type(tmpfs).__name__}'
|
||||
f"Expected tmpfs value to be either a list or a dict, found: {type(tmpfs).__name__}"
|
||||
)
|
||||
|
||||
result = {}
|
||||
@@ -205,22 +207,22 @@ def convert_service_networks(networks):
|
||||
if not networks:
|
||||
return networks
|
||||
if not isinstance(networks, list):
|
||||
raise TypeError('networks parameter must be a list.')
|
||||
raise TypeError("networks parameter must be a list.")
|
||||
|
||||
result = []
|
||||
for n in networks:
|
||||
if isinstance(n, str):
|
||||
n = {'Target': n}
|
||||
n = {"Target": n}
|
||||
result.append(n)
|
||||
return result
|
||||
|
||||
|
||||
def parse_repository_tag(repo_name):
|
||||
parts = repo_name.rsplit('@', 1)
|
||||
parts = repo_name.rsplit("@", 1)
|
||||
if len(parts) == 2:
|
||||
return tuple(parts)
|
||||
parts = repo_name.rsplit(':', 1)
|
||||
if len(parts) == 2 and '/' not in parts[1]:
|
||||
parts = repo_name.rsplit(":", 1)
|
||||
if len(parts) == 2 and "/" not in parts[1]:
|
||||
return tuple(parts)
|
||||
return repo_name, None
|
||||
|
||||
@@ -229,86 +231,81 @@ def parse_host(addr, is_win32=False, tls=False):
|
||||
# Sensible defaults
|
||||
if not addr and is_win32:
|
||||
return DEFAULT_NPIPE
|
||||
if not addr or addr.strip() == 'unix://':
|
||||
if not addr or addr.strip() == "unix://":
|
||||
return DEFAULT_UNIX_SOCKET
|
||||
|
||||
addr = addr.strip()
|
||||
|
||||
parsed_url = urlparse(addr)
|
||||
proto = parsed_url.scheme
|
||||
if not proto or any(x not in string.ascii_letters + '+' for x in proto):
|
||||
if not proto or any(x not in string.ascii_letters + "+" for x in proto):
|
||||
# https://bugs.python.org/issue754016
|
||||
parsed_url = urlparse('//' + addr, 'tcp')
|
||||
proto = 'tcp'
|
||||
parsed_url = urlparse("//" + addr, "tcp")
|
||||
proto = "tcp"
|
||||
|
||||
if proto == 'fd':
|
||||
raise errors.DockerException('fd protocol is not implemented')
|
||||
if proto == "fd":
|
||||
raise errors.DockerException("fd protocol is not implemented")
|
||||
|
||||
# These protos are valid aliases for our library but not for the
|
||||
# official spec
|
||||
if proto == 'http' or proto == 'https':
|
||||
tls = proto == 'https'
|
||||
proto = 'tcp'
|
||||
elif proto == 'http+unix':
|
||||
proto = 'unix'
|
||||
if proto == "http" or proto == "https":
|
||||
tls = proto == "https"
|
||||
proto = "tcp"
|
||||
elif proto == "http+unix":
|
||||
proto = "unix"
|
||||
|
||||
if proto not in ('tcp', 'unix', 'npipe', 'ssh'):
|
||||
raise errors.DockerException(
|
||||
f"Invalid bind address protocol: {addr}"
|
||||
)
|
||||
if proto not in ("tcp", "unix", "npipe", "ssh"):
|
||||
raise errors.DockerException(f"Invalid bind address protocol: {addr}")
|
||||
|
||||
if proto == 'tcp' and not parsed_url.netloc:
|
||||
if proto == "tcp" and not parsed_url.netloc:
|
||||
# "tcp://" is exceptionally disallowed by convention;
|
||||
# omitting a hostname for other protocols is fine
|
||||
raise errors.DockerException(
|
||||
f'Invalid bind address format: {addr}'
|
||||
)
|
||||
raise errors.DockerException(f"Invalid bind address format: {addr}")
|
||||
|
||||
if any([
|
||||
parsed_url.params, parsed_url.query, parsed_url.fragment,
|
||||
parsed_url.password
|
||||
]):
|
||||
raise errors.DockerException(
|
||||
f'Invalid bind address format: {addr}'
|
||||
)
|
||||
if any(
|
||||
[parsed_url.params, parsed_url.query, parsed_url.fragment, parsed_url.password]
|
||||
):
|
||||
raise errors.DockerException(f"Invalid bind address format: {addr}")
|
||||
|
||||
if parsed_url.path and proto == 'ssh':
|
||||
if parsed_url.path and proto == "ssh":
|
||||
raise errors.DockerException(
|
||||
f'Invalid bind address format: no path allowed for this protocol: {addr}'
|
||||
f"Invalid bind address format: no path allowed for this protocol: {addr}"
|
||||
)
|
||||
else:
|
||||
path = parsed_url.path
|
||||
if proto == 'unix' and parsed_url.hostname is not None:
|
||||
if proto == "unix" and parsed_url.hostname is not None:
|
||||
# For legacy reasons, we consider unix://path
|
||||
# to be valid and equivalent to unix:///path
|
||||
path = '/'.join((parsed_url.hostname, path))
|
||||
path = "/".join((parsed_url.hostname, path))
|
||||
|
||||
netloc = parsed_url.netloc
|
||||
if proto in ('tcp', 'ssh'):
|
||||
if proto in ("tcp", "ssh"):
|
||||
port = parsed_url.port or 0
|
||||
if port <= 0:
|
||||
port = 22 if proto == 'ssh' else (2375 if tls else 2376)
|
||||
netloc = f'{parsed_url.netloc}:{port}'
|
||||
port = 22 if proto == "ssh" else (2375 if tls else 2376)
|
||||
netloc = f"{parsed_url.netloc}:{port}"
|
||||
|
||||
if not parsed_url.hostname:
|
||||
netloc = f'{DEFAULT_HTTP_HOST}:{port}'
|
||||
netloc = f"{DEFAULT_HTTP_HOST}:{port}"
|
||||
|
||||
# Rewrite schemes to fit library internals (requests adapters)
|
||||
if proto == 'tcp':
|
||||
if proto == "tcp":
|
||||
proto = f"http{'s' if tls else ''}"
|
||||
elif proto == 'unix':
|
||||
proto = 'http+unix'
|
||||
elif proto == "unix":
|
||||
proto = "http+unix"
|
||||
|
||||
if proto in ('http+unix', 'npipe'):
|
||||
return f"{proto}://{path}".rstrip('/')
|
||||
return urlunparse(URLComponents(
|
||||
scheme=proto,
|
||||
netloc=netloc,
|
||||
url=path,
|
||||
params='',
|
||||
query='',
|
||||
fragment='',
|
||||
)).rstrip('/')
|
||||
if proto in ("http+unix", "npipe"):
|
||||
return f"{proto}://{path}".rstrip("/")
|
||||
return urlunparse(
|
||||
URLComponents(
|
||||
scheme=proto,
|
||||
netloc=netloc,
|
||||
url=path,
|
||||
params="",
|
||||
query="",
|
||||
fragment="",
|
||||
)
|
||||
).rstrip("/")
|
||||
|
||||
|
||||
def parse_devices(devices):
|
||||
@@ -318,10 +315,8 @@ def parse_devices(devices):
|
||||
device_list.append(device)
|
||||
continue
|
||||
if not isinstance(device, str):
|
||||
raise errors.DockerException(
|
||||
f'Invalid device type {type(device)}'
|
||||
)
|
||||
device_mapping = device.split(':')
|
||||
raise errors.DockerException(f"Invalid device type {type(device)}")
|
||||
device_mapping = device.split(":")
|
||||
if device_mapping:
|
||||
path_on_host = device_mapping[0]
|
||||
if len(device_mapping) > 1:
|
||||
@@ -331,27 +326,29 @@ def parse_devices(devices):
|
||||
if len(device_mapping) > 2:
|
||||
permissions = device_mapping[2]
|
||||
else:
|
||||
permissions = 'rwm'
|
||||
device_list.append({
|
||||
'PathOnHost': path_on_host,
|
||||
'PathInContainer': path_in_container,
|
||||
'CgroupPermissions': permissions
|
||||
})
|
||||
permissions = "rwm"
|
||||
device_list.append(
|
||||
{
|
||||
"PathOnHost": path_on_host,
|
||||
"PathInContainer": path_in_container,
|
||||
"CgroupPermissions": permissions,
|
||||
}
|
||||
)
|
||||
return device_list
|
||||
|
||||
|
||||
def kwargs_from_env(ssl_version=None, assert_hostname=None, environment=None):
|
||||
if not environment:
|
||||
environment = os.environ
|
||||
host = environment.get('DOCKER_HOST')
|
||||
host = environment.get("DOCKER_HOST")
|
||||
|
||||
# empty string for cert path is the same as unset.
|
||||
cert_path = environment.get('DOCKER_CERT_PATH') or None
|
||||
cert_path = environment.get("DOCKER_CERT_PATH") or None
|
||||
|
||||
# empty string for tls verify counts as "false".
|
||||
# Any value or 'unset' counts as true.
|
||||
tls_verify = environment.get('DOCKER_TLS_VERIFY')
|
||||
if tls_verify == '':
|
||||
tls_verify = environment.get("DOCKER_TLS_VERIFY")
|
||||
if tls_verify == "":
|
||||
tls_verify = False
|
||||
else:
|
||||
tls_verify = tls_verify is not None
|
||||
@@ -360,23 +357,25 @@ def kwargs_from_env(ssl_version=None, assert_hostname=None, environment=None):
|
||||
params = {}
|
||||
|
||||
if host:
|
||||
params['base_url'] = host
|
||||
params["base_url"] = host
|
||||
|
||||
if not enable_tls:
|
||||
return params
|
||||
|
||||
if not cert_path:
|
||||
cert_path = os.path.join(os.path.expanduser('~'), '.docker')
|
||||
cert_path = os.path.join(os.path.expanduser("~"), ".docker")
|
||||
|
||||
if not tls_verify and assert_hostname is None:
|
||||
# assert_hostname is a subset of TLS verification,
|
||||
# so if it is not set already then set it to false.
|
||||
assert_hostname = False
|
||||
|
||||
params['tls'] = TLSConfig(
|
||||
client_cert=(os.path.join(cert_path, 'cert.pem'),
|
||||
os.path.join(cert_path, 'key.pem')),
|
||||
ca_cert=os.path.join(cert_path, 'ca.pem'),
|
||||
params["tls"] = TLSConfig(
|
||||
client_cert=(
|
||||
os.path.join(cert_path, "cert.pem"),
|
||||
os.path.join(cert_path, "key.pem"),
|
||||
),
|
||||
ca_cert=os.path.join(cert_path, "ca.pem"),
|
||||
verify=tls_verify,
|
||||
ssl_version=ssl_version,
|
||||
assert_hostname=assert_hostname,
|
||||
@@ -389,13 +388,12 @@ def convert_filters(filters):
|
||||
result = {}
|
||||
for k, v in filters.items():
|
||||
if isinstance(v, bool):
|
||||
v = 'true' if v else 'false'
|
||||
v = "true" if v else "false"
|
||||
if not isinstance(v, list):
|
||||
v = [v, ]
|
||||
result[k] = [
|
||||
str(item) if not isinstance(item, str) else item
|
||||
for item in v
|
||||
]
|
||||
v = [
|
||||
v,
|
||||
]
|
||||
result[k] = [str(item) if not isinstance(item, str) else item for item in v]
|
||||
return json.dumps(result)
|
||||
|
||||
|
||||
@@ -415,7 +413,7 @@ def parse_bytes(s):
|
||||
# without a units part. Assuming that the units are bytes.
|
||||
if suffix.isdigit():
|
||||
digits_part = s
|
||||
suffix = 'b'
|
||||
suffix = "b"
|
||||
else:
|
||||
digits_part = s[:-1]
|
||||
|
||||
@@ -424,14 +422,14 @@ def parse_bytes(s):
|
||||
digits = float(digits_part)
|
||||
except ValueError:
|
||||
raise errors.DockerException(
|
||||
f'Failed converting the string value for memory ({digits_part}) to an integer.'
|
||||
f"Failed converting the string value for memory ({digits_part}) to an integer."
|
||||
)
|
||||
|
||||
# Reconvert to long for the final result
|
||||
s = int(digits * units[suffix])
|
||||
else:
|
||||
raise errors.DockerException(
|
||||
f'The specified value for memory ({s}) should specify the units. The postfix should be one of the `b` `k` `m` `g` characters'
|
||||
f"The specified value for memory ({s}) should specify the units. The postfix should be one of the `b` `k` `m` `g` characters"
|
||||
)
|
||||
|
||||
return s
|
||||
@@ -441,7 +439,7 @@ def normalize_links(links):
|
||||
if isinstance(links, dict):
|
||||
links = links.items()
|
||||
|
||||
return [f'{k}:{v}' if v else k for k, v in sorted(links)]
|
||||
return [f"{k}:{v}" if v else k for k, v in sorted(links)]
|
||||
|
||||
|
||||
def parse_env_file(env_file):
|
||||
@@ -451,22 +449,24 @@ def parse_env_file(env_file):
|
||||
"""
|
||||
environment = {}
|
||||
|
||||
with open(env_file, 'r') as f:
|
||||
with open(env_file, "r") as f:
|
||||
for line in f:
|
||||
|
||||
if line[0] == '#':
|
||||
if line[0] == "#":
|
||||
continue
|
||||
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
parse_line = line.split('=', 1)
|
||||
parse_line = line.split("=", 1)
|
||||
if len(parse_line) == 2:
|
||||
k, v = parse_line
|
||||
environment[k] = v
|
||||
else:
|
||||
raise errors.DockerException(f'Invalid line in environment file {env_file}:\n{line}')
|
||||
raise errors.DockerException(
|
||||
f"Invalid line in environment file {env_file}:\n{line}"
|
||||
)
|
||||
|
||||
return environment
|
||||
|
||||
@@ -480,26 +480,23 @@ def format_environment(environment):
|
||||
if value is None:
|
||||
return key
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode('utf-8')
|
||||
value = value.decode("utf-8")
|
||||
|
||||
return f"{key}={value}"
|
||||
|
||||
return f'{key}={value}'
|
||||
return [format_env(*var) for var in environment.items()]
|
||||
|
||||
|
||||
def format_extra_hosts(extra_hosts, task=False):
|
||||
# Use format dictated by Swarm API if container is part of a task
|
||||
if task:
|
||||
return [
|
||||
f'{v} {k}' for k, v in sorted(extra_hosts.items())
|
||||
]
|
||||
return [f"{v} {k}" for k, v in sorted(extra_hosts.items())]
|
||||
|
||||
return [
|
||||
f'{k}:{v}' for k, v in sorted(extra_hosts.items())
|
||||
]
|
||||
return [f"{k}:{v}" for k, v in sorted(extra_hosts.items())]
|
||||
|
||||
|
||||
def create_host_config(self, *args, **kwargs):
|
||||
raise errors.DeprecatedMethod(
|
||||
'utils.create_host_config has been removed. Please use a '
|
||||
'docker.types.HostConfig object instead.'
|
||||
"utils.create_host_config has been removed. Please use a "
|
||||
"docker.types.HostConfig object instead."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user