#!/usr/bin/env python

import argparse
import os
import subprocess
import sys
import shlex
from pkg_resources import resource_string

try:
    from jinja2 import Template
except ImportError:
    jinja2_found = False
else:
    jinja2_found = True

try:
    import git
except ImportError:
    git_found = False
else:
    git_found = True


DOCKER_CONTAINER = "ansiblebox"
ANSIBLE_TESTDIR = os.getcwd()


def generate_dockerfile(context):
    rendered_dockerfile_contents = Template(
        resource_string("ansible_test", "resources/Dockerfile.j2")
    ).render(context)

    rendered_dockerfile_path = os.path.join(ANSIBLE_TESTDIR, "Dockerfile")
    with open(rendered_dockerfile_path, "wb") as f:
        f.write(rendered_dockerfile_contents)

    return rendered_dockerfile_path


def generate_ansiblecfg(context):
    rendered_ansiblecfg_contents = Template(
        resource_string("ansible_test", "resources/ansible.cfg.j2")
    ).render(context)

    rendered_ansiblecfg_path = os.path.join(ANSIBLE_TESTDIR, "ansible.cfg")
    with open(rendered_ansiblecfg_path, "wb") as f:
        f.write(rendered_ansiblecfg_contents)

    return rendered_ansiblecfg_path


def generate_inventory():
    with open(os.path.join(ANSIBLE_TESTDIR, "inventory.ini"), "wb") as f:
        f.write(resource_string("ansible_test", "resources/inventory.ini"))


def main(context):
    if not os.path.exists(ANSIBLE_TESTDIR):
        os.mkdir(ANSIBLE_TESTDIR)

    generate_dockerfile(context)
    generate_ansiblecfg(context)
    generate_inventory()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("role",
                        help="Define the role to test")
    parser.add_argument("roles_path",
                        help="Define the path to the roles (like in ansible.cfg)")
    parser.add_argument("--ansible", '-a', default="2.3.1.0",
                        help="The ansible version to install")
    parser.add_argument("--image", "-i", default="ubuntu:xenial",
                        help="The docker base image to test the role/playbook on.")

    args, extra_args = parser.parse_known_args()

    context = {
        'role': args.role,
        'roles_path': args.roles_path,
        'ansible': args.ansible,
        'image': args.image,
        'extra_args': extra_args
    }

    if not git_found:
        print("gitpython needs to be installed")
        exit()

    if not jinja2_found:
        print("the python Jinja2 module is required")
        exit()

    main(context)
    FNULL = open(os.devnull, 'w')

    dockerRunning = subprocess.Popen(shlex.split("docker inspect -f '{{.State.Running}}' " + args.role + '-role-testing'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    for line in dockerRunning.stdout:
        if "true" in line:
            subprocess.check_call(
                ['docker', 'stop', args.role + '-role-testing'], stdout=FNULL, stderr=subprocess.STDOUT
            )

    print("INFO: Docker image will be available in a few seconds. Please wait and don't interrupt the execution.")
    subprocess.check_call(
        ['docker', 'build',
         '-t', DOCKER_CONTAINER, ANSIBLE_TESTDIR, '--no-cache'], stdout=FNULL, stderr=subprocess.STDOUT
    )
    print("SUCCESS: Docker image was created successfully!")

    print("")

    print("INFO: Start to create docker container!")
    subprocess.check_call(
        ['docker', 'create',
         '--name', args.role + '-role-testing', DOCKER_CONTAINER], stdout=FNULL, stderr=subprocess.STDOUT
    )
    print("SUCCESS: Docker container created successfully!")

    print("")

    print("INFO: Starting Docker container")
    subprocess.check_call(
        ['docker', 'start',
         args.role + '-role-testing'], stdout=FNULL, stderr=subprocess.STDOUT
    )

    print("INFO: Docker container is now ready to use.")

    print("")

    print("INFO: Starting syntax check for the role!")
    AnsibleSyntaxCheck = subprocess.call(shlex.split('docker exec ' + args.role + '-role-testing ansible-playbook -i inventory.ini ' + args.roles_path + '/' + args.role + '/tests/main.yml --syntax-check'), stdout=FNULL, stderr=subprocess.STDOUT)

    print("")

    print("INFO: Starting first role run to check its functionality.")
    AnsibleFirstRun = subprocess.call(shlex.split('docker exec ' + args.role + '-role-testing ansible-playbook -i inventory.ini ' + args.roles_path + '/' + args.role + '/tests/main.yml'))

    print("")

    print("INFO: Starting idempotence test")
    IdempotenceTest = subprocess.Popen(shlex.split("docker exec " + args.role + "-role-testing" + " ansible-playbook -i inventory.ini " + args.roles_path + "/" + args.role + "/tests/main.yml"), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    for line in IdempotenceTest.stdout:
        if "changed=0" and "failed=0" in line:
            IdempotenceTest = 0

    subprocess.check_call(
        ['docker', 'stop',
         args.role +
         '-role-testing'
         ], stdout=FNULL, stderr=subprocess.STDOUT
    )

    subprocess.check_call(
        ['docker', 'rm',
         args.role +
         '-role-testing'
         ], stdout=FNULL, stderr=subprocess.STDOUT
    )

    onFailure = False

    if AnsibleSyntaxCheck != 0:
        print("FAIL: Ansible Syntax Check failed. You have to check your used syntax.")
        onFailure = True

    if IdempotenceTest != 0:
        print("FAIL: Idempotence Test failed. You have to check if nothing changes on the second run")
        onFailure = True

    if AnsibleFirstRun != 0:
        print("FAIL: The first run failed, maybe there is a templating error?")
        onFailure = True

    if onFailure:
        sys.exit(1)
    else:
        sys.exit(0)
