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

docker connection plugin: fix config docs and update to use config system #297

Merged
merged 25 commits into from
Mar 21, 2022
Merged
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
78 changes: 55 additions & 23 deletions plugins/connection/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,56 @@
R(community.docker.docker_api,ansible_collections.community.docker.docker_api_connection)
connection plugin.
options:
remote_addr:
description:
- The name of the container you want to access.
default: inventory_hostname
bcoca marked this conversation as resolved.
Show resolved Hide resolved
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_docker_host
remote_user:
description:
- The user to execute as inside the container
- If docker is not configured to allow this, the one set by docker itself will be used
bcoca marked this conversation as resolved.
Show resolved Hide resolved
vars:
- name: ansible_user
- name: ansible_docker_user
ini:
- section: defaults
key: remote_user
env:
- name: ANSIBLE_REMOTE_USER
cli:
- name: user
keyword:
- name: remote_user
docker_extra_args:
description:
- Extra arguments to pass to the docker command line
bcoca marked this conversation as resolved.
Show resolved Hide resolved
default: ''
remote_addr:
vars:
- name: ansible_docker_extra_args
ini:
- section: docker_connection
key: extra_cli_args
container_timeout:
default: 10
description:
- The name of the container you want to access.
default: inventory_hostname
- It controls how long we can wait to access reading output from the container once execution started.
bcoca marked this conversation as resolved.
Show resolved Hide resolved
env:
- name: ANSIBLE_TIMEOUT
- name: ANSIBLE_DOCKER_TIMEOUT
bcoca marked this conversation as resolved.
Show resolved Hide resolved
ini:
- key: timeout
section: defaults
- key: timeout
section: docker_connection
bcoca marked this conversation as resolved.
Show resolved Hide resolved
vars:
- name: ansible_host
- name: ansible_docker_host
- name: ansible_docker_timeout
bcoca marked this conversation as resolved.
Show resolved Hide resolved
cli:
- name: timeout
type: integer
'''

import fcntl
Expand All @@ -47,7 +80,6 @@
import subprocess
import re

import ansible.constants as C
from ansible.compat import selectors
from ansible.errors import AnsibleError, AnsibleFileNotFound
from ansible.module_utils.six.moves import shlex_quote
Expand Down Expand Up @@ -102,15 +134,15 @@ def __init__(self, play_context, new_stdin, *args, **kwargs):
# The actual user which will execute commands in docker (if known)
self.actual_user = None

if self._play_context.remote_user is not None:
if self.get_option('remote_user') is not None:
if docker_version == u'dev' or LooseVersion(docker_version) >= LooseVersion(u'1.7'):
# Support for specifying the exec user was added in docker 1.7
self.remote_user = self._play_context.remote_user
self.remote_user = self.get_option('remote_user')
self.actual_user = self.remote_user
else:
self.actual_user = self._get_docker_remote_user()

if self.actual_user != self._play_context.remote_user:
if self.actual_user != self.get_option('remote_user'):
display.warning(u'docker {0} does not support remote_user, using container default: {1}'
.format(docker_version, self.actual_user or u'?'))
elif self._display.verbosity > 2:
Expand All @@ -127,8 +159,8 @@ def _sanitize_version(version):

def _old_docker_version(self):
cmd_args = []
if self._play_context.docker_extra_args:
cmd_args += self._play_context.docker_extra_args.split(' ')
if self.get_option('docker_extra_args'):
cmd_args += self.get_option('docker_extra_args').split(' ')

old_version_subcommand = ['version']

Expand All @@ -141,8 +173,8 @@ def _old_docker_version(self):
def _new_docker_version(self):
# no result yet, must be newer Docker version
cmd_args = []
if self._play_context.docker_extra_args:
cmd_args += self._play_context.docker_extra_args.split(' ')
if self.get_option('docker_extra_args'):
cmd_args += self.get_option('docker_extra_args').split(' ')

new_version_subcommand = ['version', '--format', "'{{.Server.Version}}'"]

Expand All @@ -167,7 +199,7 @@ def _get_docker_version(self):

def _get_docker_remote_user(self):
""" Get the default user configured in the docker container """
p = subprocess.Popen([self.docker_cmd, 'inspect', '--format', '{{.Config.User}}', self._play_context.remote_addr],
p = subprocess.Popen([self.docker_cmd, 'inspect', '--format', '{{.Config.User}}', self.get_option('remote_addr')],
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

out, err = p.communicate()
Expand All @@ -189,16 +221,16 @@ def _build_exec_cmd(self, cmd):

local_cmd = [self.docker_cmd]

if self._play_context.docker_extra_args:
local_cmd += self._play_context.docker_extra_args.split(' ')
if self.get_option('docker_extra_args'):
local_cmd += self.get_option('docker_extra_args').split(' ')

local_cmd += [b'exec']

if self.remote_user is not None:
local_cmd += [b'-u', self.remote_user]

# -i is needed to keep stdin open which allows pipelining to work
local_cmd += [b'-i', self._play_context.remote_addr] + cmd
local_cmd += [b'-i', self.get_option('remote_addr') + cmd]

return local_cmd

Expand All @@ -207,7 +239,7 @@ def _connect(self, port=None):
super(Connection, self)._connect()
if not self._connected:
display.vvv(u"ESTABLISH DOCKER CONNECTION FOR USER: {0}".format(
self.actual_user or u'?'), host=self._play_context.remote_addr
self.actual_user or u'?'), host=self.get_option('remote_addr')
)
self._connected = True

Expand All @@ -217,7 +249,7 @@ def exec_command(self, cmd, in_data=None, sudoable=False):

local_cmd = self._build_exec_cmd([self._play_context.executable, '-c', cmd])

display.vvv(u"EXEC {0}".format(to_text(local_cmd)), host=self._play_context.remote_addr)
display.vvv(u"EXEC {0}".format(to_text(local_cmd)), host=self.get_option('remote_addr'))
display.debug("opening command with Popen()")

local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
Expand All @@ -240,7 +272,7 @@ def exec_command(self, cmd, in_data=None, sudoable=False):
become_output = b''
try:
while not self.become.check_success(become_output) and not self.become.check_password_prompt(become_output):
events = selector.select(self._play_context.timeout)
events = selector.select(self.get_option('container_timeout'))
if not events:
stdout, stderr = p.communicate()
raise AnsibleError('timeout waiting for privilege escalation password prompt:\n' + to_native(become_output))
Expand Down Expand Up @@ -292,7 +324,7 @@ def _prefix_login_path(self, remote_path):
def put_file(self, in_path, out_path):
""" Transfer a file from local to docker container """
super(Connection, self).put_file(in_path, out_path)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.get_option('remote_addr'))

out_path = self._prefix_login_path(out_path)
if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')):
Expand Down Expand Up @@ -325,14 +357,14 @@ def put_file(self, in_path, out_path):
def fetch_file(self, in_path, out_path):
""" Fetch a file from container to local. """
super(Connection, self).fetch_file(in_path, out_path)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.get_option('remote_addr'))

in_path = self._prefix_login_path(in_path)
# out_path is the final file path, but docker takes a directory, not a
# file path
out_dir = os.path.dirname(out_path)

args = [self.docker_cmd, "cp", "%s:%s" % (self._play_context.remote_addr, in_path), out_dir]
args = [self.docker_cmd, "cp", "%s:%s" % (self.get_option('remote_addr'), in_path), out_dir]
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]

p = subprocess.Popen(args, stdin=subprocess.PIPE,
Expand Down