ansible: tests for sudo behaviour; closes #143.
This commit is contained in:
parent
4a61527774
commit
98ee3e177a
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
||||
)
|
|
@ -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'
|
|
@ -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'
|
|
@ -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
|
Loading…
Reference in New Issue