ansible/test/integration/targets/known_hosts/tasks/main.yml
Marius Gedminas 2efb692cc4 known_hosts: support --diff (#20349)
* known_hosts: support --diff

* known_hosts: support --diff also without --check

* Add unit tests and fix incorrect diff in one corner case

Tests are good!

* Refactor for readability

* Python 3 compat

* More Python 3 compat

* Add an integration test for known_hosts

* Handle ssh-keygen -HF returning non-zero exit code

AFAICT this is a bug in ssh-keygen in some newer OpenSSH versions
(>= 6.4 probably; see commit dd9d5cc670):
when you invoke ssh-keygen with -H and -F <host> options, it always
returns exit code 1.  This is because in ssh-keygen.c there's a function
do_known_hosts() which calls

  exit (find_host && !ctx.found_key);

at the end, and find_host is 1 (because we passed -F on the command line),
but ctx.found_key is always 0.  Why is found_key always 0?  Because the
callback passed to hostkeys_foreach(), which is known_hosts_hash(),
never bothers to set found_key to 1.

* This test does not need root

* Avoid ssh-ed25519 keys in sample known_hosts file

Older versions of OpenSSH do not like them and ssh-keygen -HF
aborts with an error when it sees such keys:

  line 5 invalid key: example.net...
  /root/ansible_testing/known_hosts is not a valid known_hosts file.

* Fix Python 3 errors

Specifically, the default mode of tempfile.NamedTemporaryFile is 'w+b',
which means Python 3 wants us to write bytes objects to it -- but the
keys we have are all unicode strings.
2017-02-08 09:56:03 -05:00

169 lines
4.9 KiB
YAML

# test code for the known_hosts module
# (c) 2017, Marius Gedminas <marius@gedmin.as>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- name: copy an existing file in place
copy: src=existing_known_hosts dest="{{output_dir|expanduser}}/known_hosts"
# test addition
- name: add a new host in check mode
check_mode: yes
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: present
path: "{{output_dir|expanduser}}/known_hosts"
register: diff
- name: assert that the diff looks as expected (the key was added at the end)
assert:
that:
- 'diff.changed'
- 'diff.diff.before_header == diff.diff.after_header == output_dir|expanduser + "/known_hosts"'
- 'diff.diff.after.splitlines()[:-1] == diff.diff.before.splitlines()'
- 'diff.diff.after.splitlines()[-1] == example_org_rsa_key.strip()'
- name: add a new host
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: present
path: "{{output_dir|expanduser}}/known_hosts"
register: result
- name: get the file content
shell: cat "{{output_dir|expanduser}}/known_hosts"
register: known_hosts
- name: assert that the key was added and ordering preserved
assert:
that:
- 'result.changed'
- 'known_hosts.stdout_lines[0].startswith("example.com")'
- 'known_hosts.stdout_lines[4].startswith("# example.net")'
- 'known_hosts.stdout_lines[-1].strip() == example_org_rsa_key.strip()'
# test idempotence of addition
- name: add the same host in check mode
check_mode: yes
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: present
path: "{{output_dir|expanduser}}/known_hosts"
register: check
- name: assert that no changes were expected
assert:
that:
- 'not check.changed'
- 'check.diff.before == check.diff.after'
- name: add the same host
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: present
path: "{{output_dir|expanduser}}/known_hosts"
register: result
- name: get the file content
shell: cat "{{output_dir|expanduser}}/known_hosts"
register: known_hosts_v2
- name: assert that no changes happened
assert:
that:
- 'not result.changed'
- 'result.diff.before == result.diff.after'
- 'known_hosts.stdout == known_hosts_v2.stdout'
# test removal
- name: remove the host in check mode
check_mode: yes
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: absent
path: "{{output_dir|expanduser}}/known_hosts"
register: diff
- name: assert that the diff looks as expected (the key was removed)
assert:
that:
- 'diff.diff.before_header == diff.diff.after_header == output_dir|expanduser + "/known_hosts"'
- 'diff.diff.before.splitlines()[-1] == example_org_rsa_key.strip()'
- 'diff.diff.after.splitlines() == diff.diff.before.splitlines()[:-1]'
- name: remove the host
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: absent
path: "{{output_dir|expanduser}}/known_hosts"
register: result
- name: get the file content
shell: cat "{{output_dir|expanduser}}/known_hosts"
register: known_hosts_v3
- name: assert that the key was removed and ordering preserved
assert:
that:
- 'result.changed'
- '"example.org" not in known_hosts_v3.stdout'
- 'known_hosts_v3.stdout_lines[0].startswith("example.com")'
- 'known_hosts_v3.stdout_lines[-1].startswith("# example.net")'
# test idempotence of removal
- name: remove the same host in check mode
check_mode: yes
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: absent
path: "{{output_dir|expanduser}}/known_hosts"
register: check
- name: assert that no changes were expected
assert:
that:
- 'not check.changed'
- 'check.diff.before == check.diff.after'
- name: remove the same host
known_hosts:
name: example.org
key: "{{ example_org_rsa_key }}"
state: absent
path: "{{output_dir|expanduser}}/known_hosts"
register: result
- name: get the file content
shell: cat "{{output_dir|expanduser}}/known_hosts"
register: known_hosts_v4
- name: assert that no changes happened
assert:
that:
- 'not result.changed'
- 'result.diff.before == result.diff.after'
- 'known_hosts_v3.stdout == known_hosts_v4.stdout'