Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add use_ssh_client flag #114

Merged
merged 3 commits into from
Apr 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/114-use_ssh_client.yml
Original file line number Diff line number Diff line change
@@ -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)."
7 changes: 7 additions & 0 deletions plugins/doc_fragments/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ class ModuleDocFragment(object):
instead. If the environment variable is not set, the default value will be used.
type: bool
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:
description:
- Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
Expand Down
4 changes: 1 addition & 3 deletions plugins/inventory/docker_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,7 @@

from ansible.errors import AnsibleError
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.parsing.utils.addresses import parse_address

from ansible_collections.community.docker.plugins.module_utils.common import (
RequestException,
Expand All @@ -146,7 +144,7 @@
)

try:
from docker.errors import DockerException, APIError, NotFound
from docker.errors import DockerException, APIError
except Exception:
# missing Docker SDK for Python handled in ansible_collections.community.docker.plugins.module_utils.common
pass
Expand Down
8 changes: 8 additions & 0 deletions plugins/inventory/docker_swarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@
type: int
default: 60
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:
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
Expand Down Expand Up @@ -171,6 +178,7 @@ def _populate(self):
api_version=self.get_option('api_version'),
timeout=self.get_option('timeout'),
ssl_version=self.get_option('ssl_version'),
use_ssh_client=self.get_option('use_ssh_client'),
debug=None,
)
update_tls_hostname(raw_params)
Expand Down
100 changes: 35 additions & 65 deletions plugins/module_utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class RequestException(Exception):
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
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']),
debug=dict(type='bool', default=False)
)
Expand Down Expand Up @@ -193,75 +194,43 @@ def get_connect_params(auth, fail_function):
if auth['tls'] or auth['tls_verify']:
auth['docker_host'] = auth['docker_host'].replace('tcp://', 'https://')

if auth['tls_verify'] and auth['cert_path'] and auth['key_path']:
# TLS with certs and host verification
if auth['cacert_path']:
tls_config = _get_tls_config(client_cert=(auth['cert_path'], auth['key_path']),
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'])
result = dict(
base_url=auth['docker_host'],
version=auth['api_version'],
timeout=auth['timeout'],
)

if auth['tls_verify']:
# TLS with verify and no certs
tls_config = _get_tls_config(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'] and auth['cert_path'] and auth['key_path']:
# TLS with certs and no host verification
tls_config = _get_tls_config(client_cert=(auth['cert_path'], auth['key_path']),
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'])

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'])
# TLS with verification
tls_config = dict(
verify=True,
assert_hostname=auth['tls_hostname'],
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'])
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.get('use_ssh_client'):
if LooseVersion(docker_version) < LooseVersion('4.4.0'):
fail_function("use_ssh_client=True requires Docker SDK for Python 4.4.0 or newer")
result['use_ssh_client'] = True

# No TLS
return dict(base_url=auth['docker_host'],
version=auth['api_version'],
timeout=auth['timeout'])
return result


DOCKERPYUPGRADE_SWITCH_TO_DOCKER = "Try `pip uninstall docker-py` followed by `pip install docker`."
Expand Down Expand Up @@ -399,6 +368,7 @@ def auth_params(self):
DEFAULT_TLS_VERIFY),
timeout=self._get_value('timeout', params['timeout'], 'DOCKER_TIMEOUT',
DEFAULT_TIMEOUT_SECONDS),
use_ssh_client=self._get_value('use_ssh_client', params['use_ssh_client'], None, False),
)

update_tls_hostname(result)
Expand Down
9 changes: 6 additions & 3 deletions plugins/modules/docker_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,16 @@
default: no
timeout:
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
default: 10
use_ssh_client:
description:
- Currently ignored for this module, but might suddenly be supported later on.

extends_documentation_fragment:
- community.docker.docker
- community.docker.docker.docker_py_1_documentation
- community.docker.docker
- community.docker.docker.docker_py_1_documentation


requirements:
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/targets/generic_ssh_connection/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
shippable/posix/group4
destructive
needs/root
skip/docker # we need a VM, and not a container
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
dependencies:
- setup_docker
- setup_paramiko
77 changes: 77 additions & 0 deletions tests/integration/targets/generic_ssh_connection/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions tests/integration/targets/setup_paramiko/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
dependencies:
- setup_remote_constraints
- setup_openssl # so cryptography is installed
6 changes: 6 additions & 0 deletions tests/integration/targets/setup_paramiko/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -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