ansible: tests for sudo behaviour; closes #143.

This commit is contained in:
David Wilson 2018-04-09 15:04:40 +01:00
parent 4a61527774
commit 98ee3e177a
8 changed files with 197 additions and 10 deletions

View File

@ -137,7 +137,26 @@ class Connection(ansible.plugins.connection.ConnectionBase):
def connected(self):
return self.broker is not None
def _wrap_connect(self, kwargs):
def _on_connection_error(self, msg):
raise ansible.errors.AnsibleConnectionFailure(msg)
def _on_become_error(self, msg):
# TODO: vanilla become failures yield this:
# {
# "changed": false,
# "module_stderr": "sudo: sorry, you must have a tty to run sudo\n",
# "module_stdout": "",
# "msg": "MODULE FAILURE",
# "rc": 1
# }
#
# Currently we yield this:
# {
# "msg": "EOF on stream; last 300 bytes received: 'sudo: ....\n'"
# }
raise ansible.errors.AnsibleModuleError(msg)
def _wrap_connect(self, on_error, kwargs):
dct = mitogen.service.call(
context=self.parent,
handle=ContextService.handle,
@ -146,7 +165,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
)
if dct['msg']:
raise ansible.errors.AnsibleConnectionFailure(dct['msg'])
on_error(dct['msg'])
return dct['context'], dct['home_dir']
@ -155,7 +174,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Fetch a reference to the local() Context from ContextService in the
master process.
"""
return self._wrap_connect({
return self._wrap_connect(self._on_connection_error, {
'method_name': 'local',
'python_path': self.python_path,
})
@ -165,7 +184,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Fetch a reference to an SSH Context matching the play context from
ContextService in the master process.
"""
return self._wrap_connect({
return self._wrap_connect(self._on_connection_error, {
'method_name': 'ssh',
'check_host_keys': False, # TODO
'hostname': self._play_context.remote_addr,
@ -189,7 +208,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
})
def _connect_docker(self):
return self._wrap_connect({
return self._wrap_connect(self._on_connection_error, {
'method_name': 'docker',
'container': self._play_context.remote_addr,
'python_path': self.python_path,
@ -205,7 +224,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Parent Context of the sudo Context. For Ansible, this should always
be a Context returned by _connect_ssh().
"""
return self._wrap_connect({
return self._wrap_connect(self._on_become_error, {
'method_name': 'sudo',
'username': self._play_context.become_user,
'password': self._play_context.become_pass,
@ -213,10 +232,14 @@ class Connection(ansible.plugins.connection.ConnectionBase):
'sudo_path': self.sudo_path,
'connect_timeout': self._play_context.timeout,
'via': via,
'sudo_args': shlex.split(
self._play_context.sudo_flags or
self._play_context.become_flags or ''
),
'sudo_args': [
term
for s in (
self._play_context.sudo_flags,
self._play_context.become_flags
)
for term in shlex.split(s or '')
],
})
def _connect(self):

View File

@ -4,6 +4,7 @@
#
- import_playbook: action/all.yml
- import_playbook: become/all.yml
- import_playbook: connection_loader/all.yml
- import_playbook: runner/all.yml
- import_playbook: playbook_semantics/all.yml

View File

@ -0,0 +1,6 @@
- import_playbook: sudo_flags_failure.yml
- import_playbook: sudo_nonexistent.yml
- import_playbook: sudo_nopassword.yml
- import_playbook: sudo_password.yml
- import_playbook: sudo_requiretty.yml

View File

@ -0,0 +1,23 @@
- hosts: all
any_errors_fatal: true
tasks:
- name: integration/become/sudo_flags_failure.yml
assert:
that: true
- name: Verify behaviour for bad sudo flags.
shell: whoami
become: true
ignore_errors: true
register: out
vars:
ansible_become_flags: --derps
- debug: msg={{out}}
- name: Verify raw module output.
assert:
that:
- out.failed
- |
('sudo: no such option: --derps' in out.msg) or
("sudo: unrecognized option `--derps'" in out.module_stderr)

View File

@ -0,0 +1,21 @@
- hosts: all
any_errors_fatal: true
tasks:
- name: integration/become/sudo_nonexistent.yml
assert:
that: true
- name: Verify behaviour for non-existent accounts.
shell: whoami
become: true
become_user: slartibartfast
ignore_errors: true
register: out
- name: Verify raw module output.
assert:
that: |
out.failed and (
('sudo: unknown user: slartibartfast' in out.msg) or
('sudo: unknown user: slartibartfast' in out.module_stderr)
)

View File

@ -0,0 +1,26 @@
# Verify passwordless sudo behaviour in various cases.
- hosts: all
any_errors_fatal: true
tasks:
- name: integration/become/sudo_basic.yml
assert:
that: true
- name: Verify we aren't root
shell: whoami
register: out
- assert:
that:
- out.stdout != 'root'
- name: Ensure passwordless sudo to root succeeds.
shell: whoami
become: true
become_user: root
register: out
- assert:
that:
- out.stdout == 'root'

View File

@ -0,0 +1,50 @@
# Verify passwordful sudo behaviour
- hosts: all
any_errors_fatal: true
tasks:
- name: integration/become/sudo_password.yml
assert:
that: true
- name: Ensure password sudo absent.
shell: whoami
become: true
become_user: mitogen__pw_required
register: out
ignore_errors: true
- assert:
that: |
out.failed and (
('password is required' in out.msg) or
('password is required' in out.module_stderr)
)
- name: Ensure password sudo incorrect.
shell: whoami
become: true
become_user: mitogen__pw_required
register: out
vars:
ansible_become_pass: nopes
ignore_errors: true
- assert:
that: |
out.failed and (
('Incorrect sudo password' in out.msg) or
('sudo password is incorrect' in out.msg)
)
- name: Ensure password sudo succeeds.
shell: whoami
become: true
become_user: mitogen__pw_required
register: out
vars:
ansible_become_pass: mitogen__password
- assert:
that:
- out.stdout == 'mitogen__pw_required'

View File

@ -0,0 +1,37 @@
# Verify requiretty support
- hosts: all
any_errors_fatal: true
tasks:
- name: integration/become/sudo_requiretty.yml
assert:
that: true
- name: Verify we can login to a non-passworded requiretty account
shell: whoami
become: true
become_user: mitogen__require_tty
register: out
when: is_mitogen
- assert:
that:
- out.stdout == 'mitogen__require_tty'
when: is_mitogen
# ---------------
- name: Verify we can login to a passworded requiretty account
shell: whoami
become: true
become_user: mitogen__require_tty_pw_required
vars:
ansible_become_pass: mitogen__password
register: out
when: is_mitogen
- assert:
that:
- out.stdout == 'mitogen__require_tty_pw_required'
when: is_mitogen