mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-03-15 19:58:28 +00:00
Add use_ssh_client flag (#114)
* Add use_ssh_client option. * Unrelated cleanup (unneeded imports). * Add SSH connection tests.
This commit is contained in:
parent
bc096a9ec9
commit
e9b10ee0a4
2
changelogs/fragments/114-use_ssh_client.yml
Normal file
2
changelogs/fragments/114-use_ssh_client.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
minor_changes:
|
||||||
|
- "Add the ``use_ssh_client`` option to most docker modules and plugins (https://github.com/ansible-collections/community.docker/issues/108, https://github.com/ansible-collections/community.docker/pull/114)."
|
||||||
@ -80,6 +80,13 @@ options:
|
|||||||
instead. If the environment variable is not set, the default value will be used.
|
instead. If the environment variable is not set, the default value will be used.
|
||||||
type: bool
|
type: bool
|
||||||
default: no
|
default: no
|
||||||
|
use_ssh_client:
|
||||||
|
description:
|
||||||
|
- For SSH transports, use the C(ssh) CLI tool instead of paramiko.
|
||||||
|
- Requires Docker SDK for Python 4.4.0 or newer.
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
version_added: 1.5.0
|
||||||
validate_certs:
|
validate_certs:
|
||||||
description:
|
description:
|
||||||
- Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
|
- Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
|
||||||
|
|||||||
@ -134,9 +134,7 @@ import re
|
|||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import update_tls_hostname, get_connect_params
|
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||||
from ansible.parsing.utils.addresses import parse_address
|
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
RequestException,
|
RequestException,
|
||||||
@ -146,7 +144,7 @@ from ansible_collections.community.docker.plugins.plugin_utils.common import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from docker.errors import DockerException, APIError, NotFound
|
from docker.errors import DockerException, APIError
|
||||||
except Exception:
|
except Exception:
|
||||||
# missing Docker SDK for Python handled in ansible_collections.community.docker.plugins.module_utils.common
|
# missing Docker SDK for Python handled in ansible_collections.community.docker.plugins.module_utils.common
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -86,6 +86,13 @@ DOCUMENTATION = '''
|
|||||||
type: int
|
type: int
|
||||||
default: 60
|
default: 60
|
||||||
aliases: [ time_out ]
|
aliases: [ time_out ]
|
||||||
|
use_ssh_client:
|
||||||
|
description:
|
||||||
|
- For SSH transports, use the C(ssh) CLI tool instead of paramiko.
|
||||||
|
- Requires Docker SDK for Python 4.4.0 or newer.
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
version_added: 1.5.0
|
||||||
include_host_uri:
|
include_host_uri:
|
||||||
description: Toggle to return the additional attribute C(ansible_host_uri) which contains the URI of the
|
description: Toggle to return the additional attribute C(ansible_host_uri) which contains the URI of the
|
||||||
swarm leader in format of C(tcp://172.16.0.1:2376). This value may be used without additional
|
swarm leader in format of C(tcp://172.16.0.1:2376). This value may be used without additional
|
||||||
@ -171,6 +178,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
|||||||
api_version=self.get_option('api_version'),
|
api_version=self.get_option('api_version'),
|
||||||
timeout=self.get_option('timeout'),
|
timeout=self.get_option('timeout'),
|
||||||
ssl_version=self.get_option('ssl_version'),
|
ssl_version=self.get_option('ssl_version'),
|
||||||
|
use_ssh_client=self.get_option('use_ssh_client'),
|
||||||
debug=None,
|
debug=None,
|
||||||
)
|
)
|
||||||
update_tls_hostname(raw_params)
|
update_tls_hostname(raw_params)
|
||||||
|
|||||||
@ -91,6 +91,7 @@ DOCKER_COMMON_ARGS = dict(
|
|||||||
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
|
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
|
||||||
ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
|
ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
|
||||||
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
|
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
|
||||||
|
use_ssh_client=dict(type='bool', default=False),
|
||||||
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
|
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
|
||||||
debug=dict(type='bool', default=False)
|
debug=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
@ -193,75 +194,43 @@ def get_connect_params(auth, fail_function):
|
|||||||
if auth['tls'] or auth['tls_verify']:
|
if auth['tls'] or auth['tls_verify']:
|
||||||
auth['docker_host'] = auth['docker_host'].replace('tcp://', 'https://')
|
auth['docker_host'] = auth['docker_host'].replace('tcp://', 'https://')
|
||||||
|
|
||||||
if auth['tls_verify'] and auth['cert_path'] and auth['key_path']:
|
result = dict(
|
||||||
# TLS with certs and host verification
|
base_url=auth['docker_host'],
|
||||||
if auth['cacert_path']:
|
version=auth['api_version'],
|
||||||
tls_config = _get_tls_config(client_cert=(auth['cert_path'], auth['key_path']),
|
timeout=auth['timeout'],
|
||||||
ca_cert=auth['cacert_path'],
|
)
|
||||||
verify=True,
|
|
||||||
assert_hostname=auth['tls_hostname'],
|
|
||||||
ssl_version=auth['ssl_version'],
|
|
||||||
fail_function=fail_function)
|
|
||||||
else:
|
|
||||||
tls_config = _get_tls_config(client_cert=(auth['cert_path'], auth['key_path']),
|
|
||||||
verify=True,
|
|
||||||
assert_hostname=auth['tls_hostname'],
|
|
||||||
ssl_version=auth['ssl_version'],
|
|
||||||
fail_function=fail_function)
|
|
||||||
|
|
||||||
return dict(base_url=auth['docker_host'],
|
|
||||||
tls=tls_config,
|
|
||||||
version=auth['api_version'],
|
|
||||||
timeout=auth['timeout'])
|
|
||||||
|
|
||||||
if auth['tls_verify'] and auth['cacert_path']:
|
|
||||||
# TLS with cacert only
|
|
||||||
tls_config = _get_tls_config(ca_cert=auth['cacert_path'],
|
|
||||||
assert_hostname=auth['tls_hostname'],
|
|
||||||
verify=True,
|
|
||||||
ssl_version=auth['ssl_version'],
|
|
||||||
fail_function=fail_function)
|
|
||||||
return dict(base_url=auth['docker_host'],
|
|
||||||
tls=tls_config,
|
|
||||||
version=auth['api_version'],
|
|
||||||
timeout=auth['timeout'])
|
|
||||||
|
|
||||||
if auth['tls_verify']:
|
if auth['tls_verify']:
|
||||||
# TLS with verify and no certs
|
# TLS with verification
|
||||||
tls_config = _get_tls_config(verify=True,
|
tls_config = dict(
|
||||||
assert_hostname=auth['tls_hostname'],
|
verify=True,
|
||||||
ssl_version=auth['ssl_version'],
|
assert_hostname=auth['tls_hostname'],
|
||||||
fail_function=fail_function)
|
ssl_version=auth['ssl_version'],
|
||||||
return dict(base_url=auth['docker_host'],
|
fail_function=fail_function,
|
||||||
tls=tls_config,
|
)
|
||||||
version=auth['api_version'],
|
if auth['cert_path'] and auth['key_path']:
|
||||||
timeout=auth['timeout'])
|
tls_config['client_cert'] = (auth['cert_path'], auth['key_path'])
|
||||||
|
if auth['cacert_path']:
|
||||||
|
tls_config['ca_cert'] = auth['cacert_path']
|
||||||
|
result['tls'] = _get_tls_config(**tls_config)
|
||||||
|
elif auth['tls']:
|
||||||
|
# TLS without verification
|
||||||
|
tls_config = dict(
|
||||||
|
verify=False,
|
||||||
|
ssl_version=auth['ssl_version'],
|
||||||
|
fail_function=fail_function,
|
||||||
|
)
|
||||||
|
if auth['cert_path'] and auth['key_path']:
|
||||||
|
tls_config['client_cert'] = (auth['cert_path'], auth['key_path'])
|
||||||
|
result['tls'] = _get_tls_config(**tls_config)
|
||||||
|
|
||||||
if auth['tls'] and auth['cert_path'] and auth['key_path']:
|
if auth.get('use_ssh_client'):
|
||||||
# TLS with certs and no host verification
|
if LooseVersion(docker_version) < LooseVersion('4.4.0'):
|
||||||
tls_config = _get_tls_config(client_cert=(auth['cert_path'], auth['key_path']),
|
fail_function("use_ssh_client=True requires Docker SDK for Python 4.4.0 or newer")
|
||||||
verify=False,
|
result['use_ssh_client'] = True
|
||||||
ssl_version=auth['ssl_version'],
|
|
||||||
fail_function=fail_function)
|
|
||||||
return dict(base_url=auth['docker_host'],
|
|
||||||
tls=tls_config,
|
|
||||||
version=auth['api_version'],
|
|
||||||
timeout=auth['timeout'])
|
|
||||||
|
|
||||||
if auth['tls']:
|
|
||||||
# TLS with no certs and not host verification
|
|
||||||
tls_config = _get_tls_config(verify=False,
|
|
||||||
ssl_version=auth['ssl_version'],
|
|
||||||
fail_function=fail_function)
|
|
||||||
return dict(base_url=auth['docker_host'],
|
|
||||||
tls=tls_config,
|
|
||||||
version=auth['api_version'],
|
|
||||||
timeout=auth['timeout'])
|
|
||||||
|
|
||||||
# No TLS
|
# No TLS
|
||||||
return dict(base_url=auth['docker_host'],
|
return result
|
||||||
version=auth['api_version'],
|
|
||||||
timeout=auth['timeout'])
|
|
||||||
|
|
||||||
|
|
||||||
DOCKERPYUPGRADE_SWITCH_TO_DOCKER = "Try `pip uninstall docker-py` followed by `pip install docker`."
|
DOCKERPYUPGRADE_SWITCH_TO_DOCKER = "Try `pip uninstall docker-py` followed by `pip install docker`."
|
||||||
@ -399,6 +368,7 @@ class AnsibleDockerClientBase(Client):
|
|||||||
DEFAULT_TLS_VERIFY),
|
DEFAULT_TLS_VERIFY),
|
||||||
timeout=self._get_value('timeout', params['timeout'], 'DOCKER_TIMEOUT',
|
timeout=self._get_value('timeout', params['timeout'], 'DOCKER_TIMEOUT',
|
||||||
DEFAULT_TIMEOUT_SECONDS),
|
DEFAULT_TIMEOUT_SECONDS),
|
||||||
|
use_ssh_client=self._get_value('use_ssh_client', params['use_ssh_client'], None, False),
|
||||||
)
|
)
|
||||||
|
|
||||||
update_tls_hostname(result)
|
update_tls_hostname(result)
|
||||||
|
|||||||
@ -143,13 +143,16 @@ options:
|
|||||||
default: no
|
default: no
|
||||||
timeout:
|
timeout:
|
||||||
description:
|
description:
|
||||||
- timeout in seconds for container shutdown when attached or when containers are already running.
|
- Timeout in seconds for container shutdown when attached or when containers are already running.
|
||||||
type: int
|
type: int
|
||||||
default: 10
|
default: 10
|
||||||
|
use_ssh_client:
|
||||||
|
description:
|
||||||
|
- Currently ignored for this module, but might suddenly be supported later on.
|
||||||
|
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- community.docker.docker
|
- community.docker.docker
|
||||||
- community.docker.docker.docker_py_1_documentation
|
- community.docker.docker.docker_py_1_documentation
|
||||||
|
|
||||||
|
|
||||||
requirements:
|
requirements:
|
||||||
|
|||||||
4
tests/integration/targets/generic_ssh_connection/aliases
Normal file
4
tests/integration/targets/generic_ssh_connection/aliases
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
shippable/posix/group4
|
||||||
|
destructive
|
||||||
|
needs/root
|
||||||
|
skip/docker # we need a VM, and not a container
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- setup_docker
|
||||||
|
- setup_paramiko
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
- name: Get docker daemon information directly
|
||||||
|
docker_host_info:
|
||||||
|
register: output
|
||||||
|
|
||||||
|
- name: Make sure we got information
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'output.host_info.Name is string'
|
||||||
|
- 'output.containers is not defined'
|
||||||
|
- 'output.networks is not defined'
|
||||||
|
- 'output.volumes is not defined'
|
||||||
|
- 'output.images is not defined'
|
||||||
|
- 'output.disk_usage is not defined'
|
||||||
|
|
||||||
|
- name: Show contents of ~/.ssh
|
||||||
|
command: ls -lah ~/.ssh
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Create SSH config
|
||||||
|
copy:
|
||||||
|
dest: "{{ lookup('env', 'HOME') }}/.ssh/config"
|
||||||
|
mode: '0600'
|
||||||
|
content: |
|
||||||
|
Host localhost
|
||||||
|
User root
|
||||||
|
IdentityFile ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
- name: Get docker daemon information via ssh (paramiko) to localhost
|
||||||
|
docker_host_info:
|
||||||
|
docker_host: "ssh://root@localhost"
|
||||||
|
register: output
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Make sure we got information
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'output.host_info.Name is string'
|
||||||
|
- 'output.containers is not defined'
|
||||||
|
- 'output.networks is not defined'
|
||||||
|
- 'output.volumes is not defined'
|
||||||
|
- 'output.images is not defined'
|
||||||
|
- 'output.disk_usage is not defined'
|
||||||
|
when: output is succeeded or 'Install paramiko package to enable' in output.msg
|
||||||
|
# For whatever reason, even though paramiko is installed, *sometimes* this error
|
||||||
|
# shows up. I have no idea why it sometimes works and sometimes not...
|
||||||
|
|
||||||
|
- name: Get docker daemon information via ssh (OpenSSH) to localhost
|
||||||
|
docker_host_info:
|
||||||
|
docker_host: "ssh://root@localhost"
|
||||||
|
use_ssh_client: true
|
||||||
|
register: output
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Make sure we got information
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- output is succeeded
|
||||||
|
- 'output.host_info.Name is string'
|
||||||
|
- 'output.containers is not defined'
|
||||||
|
- 'output.networks is not defined'
|
||||||
|
- 'output.volumes is not defined'
|
||||||
|
- 'output.images is not defined'
|
||||||
|
- 'output.disk_usage is not defined'
|
||||||
|
when: docker_py_version is version('4.4.0', '>=')
|
||||||
|
|
||||||
|
- name: Make sure we got information
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- output is failed
|
||||||
|
- "'use_ssh_client=True requires Docker SDK for Python 4.4.0 or newer' in output.msg"
|
||||||
|
when: docker_py_version is version('4.4.0', '<')
|
||||||
4
tests/integration/targets/setup_paramiko/meta/main.yml
Normal file
4
tests/integration/targets/setup_paramiko/meta/main.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- setup_remote_constraints
|
||||||
|
- setup_openssl # so cryptography is installed
|
||||||
6
tests/integration/targets/setup_paramiko/tasks/main.yml
Normal file
6
tests/integration/targets/setup_paramiko/tasks/main.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Install paramiko
|
||||||
|
pip:
|
||||||
|
name: "paramiko{% if cryptography_version.stdout is version('2.5.0', '<') %}<2.5.0{% endif %}"
|
||||||
|
extra_args: "-c {{ remote_constraints }}"
|
||||||
|
become: true
|
||||||
Loading…
Reference in New Issue
Block a user