mirror of
https://github.com/ansible-collections/community.docker.git
synced 2025-12-17 20:38:42 +00:00
* utils: fix IPv6 address w/ port parsing This was using a deprecated function (`urllib.splitnport`), ostensibly to work around issues with brackets on IPv6 addresses. Ironically, its usage was broken, and would result in mangled IPv6 addresses if they had a port specified in some instances. Usage of the deprecated function has been eliminated and extra test cases added where missing. All existing cases pass as-is. (The only other change to the test was to improve assertion messages.) Cherry-picked fromf16c4e1147Co-authored-by: Milas Bowman <milas.bowman@docker.com> * client: fix exception semantics in _raise_for_status We want "The above exception was the direct cause of the following exception:" instead of "During handling of the above exception, another exception occurred:" Cherry-picked frombb11197ee3Co-authored-by: Maor Kleinberger <kmaork@gmail.com> * tls: use auto-negotiated highest version Specific TLS versions are deprecated in latest Python, which causes test failures due to treating deprecation errors as warnings. Luckily, the fix here is straightforward: we can eliminate some custom version selection logic by using `PROTOCOL_TLS_CLIENT`, which is the recommended method and will select the highest TLS version supported by both client and server. Cherry-picked from56dd6de7dfCo-authored-by: Milas Bowman <milas.bowman@docker.com> * transport: fix ProxyCommand for SSH conn Cherry-picked from4e19cc48dfCo-authored-by: Guy Lichtman <glicht@users.noreply.github.com> * ssh: do not create unnecessary subshell on exec Cherry-picked frombb40ba051fCo-authored-by: liubo <liubo@uniontech.com> * ssh: reject unknown host keys when using Python SSH impl In the Secure Shell (SSH) protocol, host keys are used to verify the identity of remote hosts. Accepting unknown host keys may leave the connection open to man-in-the-middle attacks. Do not accept unknown host keys. In particular, do not set the default missing host key policy for the Paramiko library to either AutoAddPolicy or WarningPolicy. Both of these policies continue even when the host key is unknown. The default setting of RejectPolicy is secure because it throws an exception when it encounters an unknown host key. Reference: https://cwe.mitre.org/data/definitions/295.html NOTE: This only affects SSH connections using the native Python SSH implementation (Paramiko), when `use_ssh_client=False` (default). If using the system SSH client (`use_ssh_client=True`), the host configuration (e.g. `~/.ssh/config`) will apply. Cherry-picked fromd9298647d9Co-authored-by: Audun Nes <audun.nes@gmail.com> * lint: fix deprecation warnings from threading package Set `daemon` attribute instead of using `setDaemon` method that was deprecated in Python 3.10. Cherry-picked fromadf5a97b12Co-authored-by: Karthikeyan Singaravelan <tir.karthi@gmail.com> * api: preserve cause when re-raising error Use `from e` to ensure that the error context is propagated correctly. Cherry-picked from05e143429eCo-authored-by: Milas Bowman <milas.bowman@docker.com> * build: trim trailing whitespace from dockerignore entries Cherry-picked from3ee3a2486fCo-authored-by: Clément Loiselet <clement.loiselet@capgemini.com> * Improve formulation, also mention the security change as a breaking change. Co-authored-by: Milas Bowman <milas.bowman@docker.com> Co-authored-by: Maor Kleinberger <kmaork@gmail.com> Co-authored-by: Guy Lichtman <glicht@users.noreply.github.com> Co-authored-by: liubo <liubo@uniontech.com> Co-authored-by: Audun Nes <audun.nes@gmail.com> Co-authored-by: Karthikeyan Singaravelan <tir.karthi@gmail.com> Co-authored-by: Clément Loiselet <clement.loiselet@capgemini.com>
224 lines
6.0 KiB
Python
224 lines
6.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
# This code is part of the Ansible collection community.docker, but is an independent component.
|
|
# This particular file, and this file only, is based on the Docker SDK for Python (https://github.com/docker/docker-py/)
|
|
#
|
|
# Copyright (c) 2016-2022 Docker, Inc.
|
|
#
|
|
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
__metaclass__ = type
|
|
|
|
from ._import_helper import HTTPError as _HTTPError
|
|
|
|
from ansible.module_utils.six import raise_from
|
|
|
|
|
|
class DockerException(Exception):
|
|
"""
|
|
A base class from which all other exceptions inherit.
|
|
|
|
If you want to catch all errors that the Docker SDK might raise,
|
|
catch this base exception.
|
|
"""
|
|
|
|
|
|
def create_api_error_from_http_exception(e):
|
|
"""
|
|
Create a suitable APIError from requests.exceptions.HTTPError.
|
|
"""
|
|
response = e.response
|
|
try:
|
|
explanation = response.json()['message']
|
|
except ValueError:
|
|
explanation = (response.content or '').strip()
|
|
cls = APIError
|
|
if response.status_code == 404:
|
|
if explanation and ('No such image' in str(explanation) or
|
|
'not found: does not exist or no pull access'
|
|
in str(explanation) or
|
|
'repository does not exist' in str(explanation)):
|
|
cls = ImageNotFound
|
|
else:
|
|
cls = NotFound
|
|
raise_from(cls(e, response=response, explanation=explanation), e)
|
|
|
|
|
|
class APIError(_HTTPError, DockerException):
|
|
"""
|
|
An HTTP error from the API.
|
|
"""
|
|
def __init__(self, message, response=None, explanation=None):
|
|
# requests 1.2 supports response as a keyword argument, but
|
|
# requests 1.1 doesn't
|
|
super(APIError, self).__init__(message)
|
|
self.response = response
|
|
self.explanation = explanation
|
|
|
|
def __str__(self):
|
|
message = super(APIError, self).__str__()
|
|
|
|
if self.is_client_error():
|
|
message = '{0} Client Error for {1}: {2}'.format(
|
|
self.response.status_code, self.response.url,
|
|
self.response.reason)
|
|
|
|
elif self.is_server_error():
|
|
message = '{0} Server Error for {1}: {2}'.format(
|
|
self.response.status_code, self.response.url,
|
|
self.response.reason)
|
|
|
|
if self.explanation:
|
|
message = '{0} ("{1}")'.format(message, self.explanation)
|
|
|
|
return message
|
|
|
|
@property
|
|
def status_code(self):
|
|
if self.response is not None:
|
|
return self.response.status_code
|
|
|
|
def is_error(self):
|
|
return self.is_client_error() or self.is_server_error()
|
|
|
|
def is_client_error(self):
|
|
if self.status_code is None:
|
|
return False
|
|
return 400 <= self.status_code < 500
|
|
|
|
def is_server_error(self):
|
|
if self.status_code is None:
|
|
return False
|
|
return 500 <= self.status_code < 600
|
|
|
|
|
|
class NotFound(APIError):
|
|
pass
|
|
|
|
|
|
class ImageNotFound(NotFound):
|
|
pass
|
|
|
|
|
|
class InvalidVersion(DockerException):
|
|
pass
|
|
|
|
|
|
class InvalidRepository(DockerException):
|
|
pass
|
|
|
|
|
|
class InvalidConfigFile(DockerException):
|
|
pass
|
|
|
|
|
|
class InvalidArgument(DockerException):
|
|
pass
|
|
|
|
|
|
class DeprecatedMethod(DockerException):
|
|
pass
|
|
|
|
|
|
class TLSParameterError(DockerException):
|
|
def __init__(self, msg):
|
|
self.msg = msg
|
|
|
|
def __str__(self):
|
|
return self.msg + (". TLS configurations should map the Docker CLI "
|
|
"client configurations. See "
|
|
"https://docs.docker.com/engine/articles/https/ "
|
|
"for API details.")
|
|
|
|
|
|
class NullResource(DockerException, ValueError):
|
|
pass
|
|
|
|
|
|
class ContainerError(DockerException):
|
|
"""
|
|
Represents a container that has exited with a non-zero exit code.
|
|
"""
|
|
def __init__(self, container, exit_status, command, image, stderr):
|
|
self.container = container
|
|
self.exit_status = exit_status
|
|
self.command = command
|
|
self.image = image
|
|
self.stderr = stderr
|
|
|
|
err = ": {0}".format(stderr) if stderr is not None else ""
|
|
msg = ("Command '{0}' in image '{1}' returned non-zero exit "
|
|
"status {2}{3}").format(command, image, exit_status, err)
|
|
|
|
super(ContainerError, self).__init__(msg)
|
|
|
|
|
|
class StreamParseError(RuntimeError):
|
|
def __init__(self, reason):
|
|
self.msg = reason
|
|
|
|
|
|
class BuildError(DockerException):
|
|
def __init__(self, reason, build_log):
|
|
super(BuildError, self).__init__(reason)
|
|
self.msg = reason
|
|
self.build_log = build_log
|
|
|
|
|
|
class ImageLoadError(DockerException):
|
|
pass
|
|
|
|
|
|
def create_unexpected_kwargs_error(name, kwargs):
|
|
quoted_kwargs = ["'{0}'".format(k) for k in sorted(kwargs)]
|
|
text = ["{0}() ".format(name)]
|
|
if len(quoted_kwargs) == 1:
|
|
text.append("got an unexpected keyword argument ")
|
|
else:
|
|
text.append("got unexpected keyword arguments ")
|
|
text.append(', '.join(quoted_kwargs))
|
|
return TypeError(''.join(text))
|
|
|
|
|
|
class MissingContextParameter(DockerException):
|
|
def __init__(self, param):
|
|
self.param = param
|
|
|
|
def __str__(self):
|
|
return ("missing parameter: {0}".format(self.param))
|
|
|
|
|
|
class ContextAlreadyExists(DockerException):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return ("context {0} already exists".format(self.name))
|
|
|
|
|
|
class ContextException(DockerException):
|
|
def __init__(self, msg):
|
|
self.msg = msg
|
|
|
|
def __str__(self):
|
|
return (self.msg)
|
|
|
|
|
|
class ContextNotFound(DockerException):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return ("context '{0}' not found".format(self.name))
|
|
|
|
|
|
class MissingRequirementException(DockerException):
|
|
def __init__(self, msg, requirement, import_exception):
|
|
self.msg = msg
|
|
self.requirement = requirement
|
|
self.import_exception = import_exception
|
|
|
|
def __str__(self):
|
|
return (self.msg)
|