Blame tests/utils.py

Packit Service a166ed
#!/usr/bin/env python
Packit Service a166ed
Packit Service a166ed
# Authors:
Packit Service a166ed
#   Sergio Oliveira Campos <seocam@redhat.com>
Packit Service a166ed
#
Packit Service a166ed
# Copyright (C) 2020 Red Hat
Packit Service a166ed
# see file 'COPYING' for use and warranty information
Packit Service a166ed
#
Packit Service a166ed
# This program is free software; you can redistribute it and/or modify
Packit Service a166ed
# it under the terms of the GNU General Public License as published by
Packit Service a166ed
# the Free Software Foundation, either version 3 of the License, or
Packit Service a166ed
# (at your option) any later version.
Packit Service a166ed
#
Packit Service a166ed
# This program is distributed in the hope that it will be useful,
Packit Service a166ed
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a166ed
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a166ed
# GNU General Public License for more details.
Packit Service a166ed
#
Packit Service a166ed
# You should have received a copy of the GNU General Public License
Packit Service a166ed
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a166ed
Packit Service a166ed
import os
Packit Service a166ed
import pytest
Packit Service a166ed
import subprocess
Packit Service a166ed
import tempfile
Packit Service a166ed
import testinfra
Packit Service a166ed
Packit Service a166ed
from unittest import TestCase
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def is_docker_env():
Packit Service a166ed
    if os.getenv("RUN_TESTS_IN_DOCKER", "0") == "0":
Packit Service a166ed
        return False
Packit Service a166ed
    return True
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def get_ssh_password():
Packit Service a166ed
    return os.getenv("IPA_SSH_PASSWORD")
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def get_server_host():
Packit Service a166ed
    return os.getenv("IPA_SERVER_HOST")
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def get_inventory_content():
Packit Service a166ed
    """Create the content of an inventory file for a test run."""
Packit Service a166ed
    ipa_server_host = get_server_host()
Packit Service a166ed
Packit Service a166ed
    if is_docker_env():
Packit Service a166ed
        ipa_server_host += " ansible_connection=docker"
Packit Service a166ed
Packit Service a166ed
    sshpass = get_ssh_password()
Packit Service a166ed
    if sshpass:
Packit Service a166ed
        ipa_server_host += " ansible_ssh_pass=%s" % sshpass
Packit Service a166ed
Packit Service a166ed
    lines = [
Packit Service a166ed
        "[ipaserver]",
Packit Service a166ed
        ipa_server_host,
Packit Service a166ed
        "[ipaserver:vars]",
Packit Service a166ed
        "ipaserver_domain=test.local",
Packit Service a166ed
        "ipaserver_realm=TEST.LOCAL",
Packit Service a166ed
    ]
Packit Service a166ed
    return "\n".join(lines).encode("utf8")
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def get_test_name_from_playbook_path(playbook):
Packit Service a166ed
    """
Packit Service a166ed
    Create a test name based of a playbook path.
Packit Service a166ed
Packit Service a166ed
    For example:
Packit Service a166ed
        Input: /home/johndoe/ansible-freeipa/tests/dnszone/test_dnszone_mod.yml
Packit Service a166ed
        Output: dnszone_test_dnszone_mod
Packit Service a166ed
    """
Packit Service a166ed
    playbook_abspath = os.path.abspath(playbook)
Packit Service a166ed
    playbook_rel_to_tests_dir = playbook_abspath.replace(SCRIPT_DIR, "")
Packit Service a166ed
    playbook_slug = playbook_rel_to_tests_dir.strip("/").replace("/", "_")
Packit Service a166ed
    return os.path.splitext(playbook_slug)[0]
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def write_logs(result, test_name):
Packit Service a166ed
    """Write logs of a ansible run logs to `test/logs/`."""
Packit Service a166ed
    log_dir = os.path.join(SCRIPT_DIR, "logs")
Packit Service a166ed
    if not os.path.exists(log_dir):
Packit Service a166ed
        os.makedirs(log_dir)
Packit Service a166ed
Packit Service a166ed
    # Write stdout log for test
Packit Service a166ed
    log_path = os.path.join(log_dir, "ansible_" + test_name + ".log")
Packit Service a166ed
    with open(log_path, "w") as log_file:
Packit Service a166ed
        log_file.write(result.stdout.decode("utf-8"))
Packit Service a166ed
Packit Service a166ed
    # Write stderr log for test
Packit Service a166ed
    error_log_path = os.path.join(log_dir, test_name + "-error.log")
Packit Service a166ed
    with open(error_log_path, "w") as log_file:
Packit Service a166ed
        log_file.write(result.stderr.decode("utf-8"))
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def _run_playbook(playbook):
Packit Service a166ed
    """
Packit Service a166ed
    Create a inventory using a temporary file and run ansible using it.
Packit Service a166ed
Packit Service a166ed
    The logs of the run will be placed in `tests/logs/`.
Packit Service a166ed
    """
Packit Service a166ed
    with tempfile.NamedTemporaryFile() as inventory_file:
Packit Service a166ed
        inventory_file.write(get_inventory_content())
Packit Service a166ed
        inventory_file.flush()
Packit Service a166ed
        cmd = [
Packit Service a166ed
            "ansible-playbook",
Packit Service a166ed
            "-i",
Packit Service a166ed
            inventory_file.name,
Packit Service a166ed
            playbook,
Packit Service a166ed
        ]
Packit Service a166ed
        process = subprocess.run(
Packit Service a166ed
            cmd, cwd=SCRIPT_DIR, stdout=subprocess.PIPE, stderr=subprocess.PIPE
Packit Service a166ed
        )
Packit Service a166ed
    test_name = get_test_name_from_playbook_path(playbook)
Packit Service a166ed
    write_logs(process, test_name)
Packit Service a166ed
Packit Service a166ed
    return process
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def run_playbook(playbook, allow_failures=False):
Packit Service a166ed
    """
Packit Service a166ed
    Run an Ansible playbook and assert the return code.
Packit Service a166ed
Packit Service a166ed
    Call ansible (using _run_playbook function) and assert the result of
Packit Service a166ed
    the execution.
Packit Service a166ed
Packit Service a166ed
    In case of failure the tail of the error message will be displayed
Packit Service a166ed
    as an assertion message.
Packit Service a166ed
Packit Service a166ed
    The full log of the execution will be available in the directory
Packit Service a166ed
    `tests/logs/`.
Packit Service a166ed
    """
Packit Service a166ed
    result = _run_playbook(playbook)
Packit Service a166ed
Packit Service a166ed
    if allow_failures:
Packit Service a166ed
        return result
Packit Service a166ed
Packit Service a166ed
    status_code_msg = "ansible-playbook return code: {}".format(
Packit Service a166ed
        result.returncode
Packit Service a166ed
    )
Packit Service a166ed
    assert_msg = "\n".join(
Packit Service a166ed
        [
Packit Service a166ed
            "",
Packit Service a166ed
            "-" * 30 + " Captured stdout " + "-" * 30,
Packit Service a166ed
            result.stdout.decode("utf8"),
Packit Service a166ed
            "-" * 30 + " Captured stderr " + "-" * 30,
Packit Service a166ed
            result.stderr.decode("utf8"),
Packit Service a166ed
            "-" * 30 + " Playbook Return Code " + "-" * 30,
Packit Service a166ed
            status_code_msg,
Packit Service a166ed
        ]
Packit Service a166ed
    )
Packit Service a166ed
Packit Service a166ed
    # Need to get the last bytes of msg otherwise Azure
Packit Service a166ed
    #   will cut it out.
Packit Service a166ed
    assert result.returncode == 0, assert_msg[-2500:]
Packit Service a166ed
Packit Service a166ed
    return result
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def list_test_yaml(dir_path):
Packit Service a166ed
    """
Packit Service a166ed
    List the test playbooks inside a given directory.
Packit Service a166ed
Packit Service a166ed
    A test playbook is any file inside the directory which the name starts with
Packit Service a166ed
    `test_` and the extension is `.yml`.
Packit Service a166ed
    """
Packit Service a166ed
    yamls = []
Packit Service 6e1210
    for root, _dirs, files in os.walk(dir_path):
Packit Service a166ed
        for yaml_name in files:
Packit Service a166ed
            if yaml_name.startswith("test_") and yaml_name.endswith(".yml"):
Packit Service a166ed
                test_yaml_path = os.path.join(root, yaml_name)
Packit Service a166ed
                yamls.append(
Packit Service a166ed
                    {
Packit Service a166ed
                        "path": test_yaml_path,
Packit Service a166ed
                        "name": yaml_name.split(".")[0],
Packit Service a166ed
                    }
Packit Service a166ed
                )
Packit Service a166ed
    return yamls
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def get_test_playbooks():
Packit Service a166ed
    """
Packit Service a166ed
    Get playbook tests grouped by first level directory.
Packit Service a166ed
Packit Service a166ed
    This function visits the first level of directories inside `tests/` and
Packit Service a166ed
    look for ansible playbooks on them.
Packit Service a166ed
Packit Service a166ed
    Returns a dict with the directories found in `tests/` as key and a
Packit Service a166ed
    list of test playbook files inside of it.
Packit Service a166ed
Packit Service a166ed
    A test playbook is any file inside the directory which the name starts with
Packit Service a166ed
    `test_` and the extension is `.yml`.
Packit Service a166ed
    """
Packit Service a166ed
    test_dirs = os.listdir(SCRIPT_DIR)
Packit Service a166ed
    groups = {}
Packit Service a166ed
    for test_group_dir in sorted(test_dirs):
Packit Service a166ed
        group_dir_path = os.path.join(SCRIPT_DIR, test_group_dir)
Packit Service a166ed
        if not os.path.isdir(group_dir_path):
Packit Service a166ed
            continue
Packit Service a166ed
        yamls = list_test_yaml(group_dir_path)
Packit Service a166ed
        if yamls:
Packit Service a166ed
            groups[test_group_dir] = yamls
Packit Service a166ed
    return groups
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def kinit_admin(host, admin="admin", password="SomeADMINpassword"):
Packit Service a166ed
    return host.run_test("kinit " + admin + "<<< " + password)
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
def kdestroy(host):
Packit Service a166ed
    return host.run_test("kdestroy -A")
Packit Service a166ed
Packit Service a166ed
Packit Service a166ed
class AnsibleFreeIPATestCase(TestCase):
Packit Service a166ed
    def setUp(self):
Packit Service a166ed
        if is_docker_env():
Packit Service a166ed
            protocol = "docker://"
Packit Service a166ed
            user = ""
Packit Service a166ed
            ssh_identity_file = None
Packit Service a166ed
        else:
Packit Service a166ed
            protocol = "ssh://"
Packit Service a166ed
Packit Service a166ed
            password = get_ssh_password() or ""
Packit Service a166ed
            if password:
Packit Service a166ed
                password = ":" + password
Packit Service a166ed
Packit Service a166ed
            current_user = os.getenv("USER")
Packit Service a166ed
            ansible_user = os.getenv("ANSIBLE_REMOTE_USER", current_user)
Packit Service a166ed
            user = ansible_user + password + "@"
Packit Service a166ed
            ssh_identity_file = os.getenv("ANSIBLE_PRIVATE_KEY_FILE", None)
Packit Service a166ed
Packit Service a166ed
        host_connection_info = protocol + user + get_server_host()
Packit Service a166ed
        self.master = testinfra.get_host(
Packit Service a166ed
            host_connection_info, ssh_identity_file=ssh_identity_file,
Packit Service a166ed
        )
Packit Service a166ed
Packit Service a166ed
    def run_playbook(self, playbook, allow_failures=False):
Packit Service a166ed
        return run_playbook(playbook, allow_failures)
Packit Service a166ed
Packit Service a166ed
    def run_playbook_with_exp_msg(self, playbook, expected_msg):
Packit Service a166ed
        result = self.run_playbook(playbook, allow_failures=True)
Packit Service a166ed
        assert (
Packit Service a166ed
            expected_msg in result.stdout.decode("utf8")
Packit Service a166ed
            or
Packit Service a166ed
            expected_msg in result.stderr.decode("utf8")
Packit Service a166ed
        )
Packit Service a166ed
Packit Service a166ed
    def check_details(self, expected_output, cmd, extra_cmds=None):
Packit Service a166ed
        cmd = "ipa " + cmd
Packit Service a166ed
        if extra_cmds:
Packit Service a166ed
            cmd += " " + " ".join(extra_cmds)
Packit Service a166ed
        kinit_admin(self.master)
Packit Service a166ed
        res = self.master.run(cmd)
Packit Service a166ed
        if res.rc != 0:
Packit Service a166ed
            for output in expected_output:
Packit Service a166ed
                assert output in res.stderr
Packit Service a166ed
        else:
Packit Service a166ed
            for output in expected_output:
Packit Service a166ed
                assert output in res.stdout
Packit Service a166ed
        kdestroy(self.master)
Packit Service a166ed
Packit Service a166ed
    def check_notexists(self, members, cmd, extra_cmds=None):
Packit Service a166ed
        cmd = "ipa " + cmd
Packit Service a166ed
        if extra_cmds:
Packit Service a166ed
            cmd += " " + " ".join(extra_cmds)
Packit Service a166ed
        kinit_admin(self.master)
Packit Service a166ed
        res = self.master.run(cmd)
Packit Service a166ed
        for member in members:
Packit Service a166ed
            assert member not in res.stdout
Packit Service a166ed
        kdestroy(self.master)
Packit Service a166ed
Packit Service a166ed
    def mark_xfail_using_ansible_freeipa_version(self, version, reason):
Packit Service a166ed
        package = self.master.package("ansible-freeipa")
Packit Service a166ed
Packit Service a166ed
        if not package.is_installed:
Packit Service a166ed
            return
Packit Service a166ed
Packit Service a166ed
        if package.version == version:
Packit Service a166ed
            pytest.xfail(reason)