#!/usr/bin/env python2
from cfme.utils.conf import docker as docker_conf
from cfme.utils.net import random_port, my_ip_address
import argparse
import fauxfactory
import requests
import os
import os.path
import docker
import re
import subprocess
import sys
import yaml
from six.moves.urllib.parse import urlsplit
def _dgci(d, key):
    # dgci = dict get case-insensitive
    keymap = {k.lower(): k for k in d.keys()}
    return d.get(keymap[key.lower()])
def _name(docker_info):
    return _dgci(docker_info, 'name').strip('/')
if os.getenv("DOCKER_MACHINE_NAME", "None") == "None":
    dc = docker.Client(base_url='unix://var/run/docker.sock',
                       version='1.12',
                       timeout=10)
else:
    from docker.utils import kwargs_from_env
    dc = docker.Client(version='1.12',
                       timeout=10,
                       **kwargs_from_env(assert_hostname=False))
class DockerInstance(object):
    def process_bindings(self, bindings):
        self.port_bindings = {}
        self.ports = []
        for bind in bindings:
            self.port_bindings[bindings[bind][0]] = bindings[bind][1]
            print("  {}: {}".format(bind, bindings[bind][1]))
            self.ports.append(bindings[bind][1])
    def wait(self):
        if not self.dry_run:
            dc.wait(self.container_id)
        else:
            print("Waiting for container")
    def stop(self):
        if not self.dry_run:
            dc.stop(self.container_id)
        else:
            print("Stopping container")
    def remove(self):
        if not self.dry_run:
            dc.remove_container(self.container_id, v=True)
        else:
            print("Removing container")
    def kill(self):
        if not self.dry_run:
            dc.kill(self.container_id)
        else:
            print("Killing container")
class SeleniumDocker(DockerInstance):
    def __init__(self, bindings, image, dry_run=False):
        self.dry_run = dry_run
        sel_name = fauxfactory.gen_alphanumeric(8)
        if not self.dry_run:
            sel_create_info = dc.create_container(image, tty=True, name=sel_name)
            self.container_id = _dgci(sel_create_info, 'id')
            sel_container_info = dc.inspect_container(self.container_id)
            self.sel_name = _name(sel_container_info)
        else:
            self.sel_name = "SEL_FF_CHROME_TEST"
        self.process_bindings(bindings)
    def run(self):
        if not self.dry_run:
            dc.start(self.container_id, privileged=True, port_bindings=self.port_bindings)
        else:
            print("Dry run running sel_ff_chrome")
class PytestDocker(DockerInstance):
    def __init__(self, name, bindings, env, log_path, links, pytest_con, artifactor_dir,
                 dry_run=False):
        self.dry_run = dry_run
        self.links = links
        self.log_path = log_path
        self.artifactor_dir = artifactor_dir
        self.process_bindings(bindings)
        if not self.dry_run:
            pt_name = name
            pt_create_info = dc.create_container(pytest_con, tty=True,
                                                 name=pt_name, environment=env,
                                                 command='sh /setup.sh',
                                                 volumes=[artifactor_dir],
                                                 ports=self.ports)
            self.container_id = _dgci(pt_create_info, 'id')
            pt_container_info = dc.inspect_container(self.container_id)
            pt_name = _name(pt_container_info)
    def run(self):
        if not self.dry_run:
            dc.start(self.container_id, privileged=True, links=self.links,
                     binds={self.log_path: {'bind': self.artifactor_dir, 'ro': False}},
                     port_bindings=self.port_bindings)
        else:
            print("Dry run running pytest")
class DockerBot(object):
    def __init__(self, **args):
        links = []
        self.args = args
        self.base_branch = 'master'
        self.validate_args()
        self.display_banner()
        self.process_appliance()
        self.cache_files()
        self.create_pytest_command()
        if not self.args['use_wharf']:
            self.sel_vnc_port = random_port()
            sel = SeleniumDocker(bindings={'VNC_PORT': (5999, self.sel_vnc_port)},
                                 image=self.args['selff'], dry_run=self.args['dry_run'])
            sel.run()
            sel_container_name = sel.sel_name
            links = [(sel_container_name, 'selff')]
        self.pytest_name = self.args['test_id']
        self.create_pytest_envvars()
        self.handle_pr()
        self.log_path = self.create_log_path()
        self.pytest_bindings = self.create_pytest_bindings()
        if self.args['dry_run']:
            for i in self.env_details:
                print('export {}="{}"'.format(i, self.env_details[i]))
            print(self.env_details)
        pytest = PytestDocker(name=self.pytest_name, bindings=self.pytest_bindings,
                              env=self.env_details, log_path=self.log_path,
                              links=links,
                              pytest_con=self.args['pytest_con'],
                              artifactor_dir=self.args['artifactor_dir'],
                              dry_run=self.args['dry_run'])
        pytest.run()
        if not self.args['nowait']:
            self.handle_watch()
            if self.args['dry_run']:
                with open(os.path.join(self.log_path, 'setup.txt'), "w") as f:
                    f.write("finshed")
            try:
                pytest.wait()
            except KeyboardInterrupt:
                print("  TEST INTERRUPTED....KILLING ALL THE THINGS")
                pass
            pytest.kill()
            pytest.remove()
            if not self.args['use_wharf']:
                sel.kill()
                sel.remove()
            self.handle_output()
    def cache_files(self):
        if self.args['pr']:
            self.modified_files = self.find_files_by_pr(self.args['pr'])
            if self.requirements_update:
                self.args['update_pip'] = True
    def get_base_branch(self, pr):
        token = self.args['gh_token']
        owner = self.args['gh_owner']
        repo = self.args['gh_repo']
        if token:
            headers = {'Authorization': 'token {}'.format(token)}
            r = requests.get(
                'https://api.github.com/repos/{}/{}/pulls/{}'.format(owner, repo, pr),
                headers=headers)
            return r.json()['base']['ref']
    def get_dev_branch(self, pr=None):
        token = self.args['gh_token']
        owner = self.args['gh_dev_owner']
        repo = self.args['gh_dev_repo']
        if token:
            headers = {'Authorization': 'token {}'.format(token)}
            r = requests.get(
                'https://api.github.com/repos/{}/{}/pulls/{}'.format(owner, repo, pr),
                headers=headers)
            user, user_branch = r.json()['head']['label'].split(":")
        return "https://github.com/{}/{}.git".format(user, repo), user_branch
    def get_pr_metadata(self, pr=None):
        token = self.args['gh_token']
        owner = self.args['gh_owner']
        repo = self.args['gh_repo']
        if token:
            headers = {'Authorization': 'token {}'.format(token)}
            r = requests.get(
                'https://api.github.com/repos/{}/{}/pulls/{}'.format(owner, repo, pr),
                headers=headers)
            body = r.json()['body'] or ""
            metadata = re.findall("{{(.*?)}}", body)
            if not metadata:
                return {}
            else:
                ydata = yaml.safe_load(metadata[0])
                return ydata
    def find_files_by_pr(self, pr=None):
        self.requirements_update = False
        files = []
        token = self.args['gh_token']
        owner = self.args['gh_owner']
        repo = self.args['gh_repo']
        if token:
            headers = {'Authorization': 'token {}'.format(token)}
            page = 1
            while True:
                r = requests.get(
                    'https://api.github.com/repos/{}/{}/pulls/{}/files?page={}'.format(
                        owner, repo, pr, page),
                    headers=headers)
                try:
                    if not r.json():
                        break
                    for filen in r.json():
                        if filen['status'] != "deleted" and filen['status'] != "removed":
                            if filen['filename'].startswith('cfme/tests') or \
                               filen['filename'].startswith('utils/tests'):
                                files.append(filen['filename'])
                        if filen['filename'].endswith('requirements/frozen.txt'):
                            self.requirements_update = True
                except:
                    return None
                page += 1
            return files
    def check_arg(self, name, default):
        self.args[name] = self.args.get(name)
        if not self.args[name]:
            self.args[name] = docker_conf.get(name, default)
    def validate_args(self):
        ec = 0
        appliance = self.args.get('appliance', None)
        if self.args.get('appliance_name', None) and not appliance:
            self.args['appliance'] = docker_conf['appliances'][self.args['appliance_name']]
        self.check_arg('nowait', False)
        self.check_arg('banner', False)
        self.check_arg('watch', False)
        self.check_arg('output', True)
        self.check_arg('dry_run', False)
        self.check_arg('server_ip', None)
        if not self.args['server_ip']:
            self.args['server_ip'] = my_ip_address()
        self.check_arg('sprout', False)
        self.check_arg('provision_appliance', False)
        if self.args['provision_appliance']:
            if not self.args['provision_template'] or not self.args['provision_provider'] or \
               not self.args['provision_vm_name']:
                print("You don't have all the required options to provision an appliance")
                ec += 1
        self.check_arg('sprout_stream', None)
        if self.args['sprout'] and not self.args['sprout_stream']:
            print("You need to supply a stream for sprout")
            ec += 1
        self.check_arg('appliance_name', None)
        self.check_arg('appliance', None)
        if not self.args['appliance_name'] != self.args['appliance'] and \
           not self.args['provision_appliance'] and not self.args['sprout']:
            print("You must supply either an appliance OR an appliance name from config")
            ec += 1
        self.check_arg('branch', 'origin/master')
        self.check_arg('pr', None)
        self.check_arg('dev_pr', None)
        self.check_arg('cfme_repo', None)
        self.check_arg('cfme_repo_dir', '/cfme_tests_te')
        self.check_arg('cfme_cred_repo', None)
        self.check_arg('cfme_cred_repo_dir', '/cfme-qe-yamls')
        self.check_arg('dev_repo', None)
        if not self.args['cfme_repo']:
            print("You must supply a CFME REPO")
            ec += 1
        if not self.args['cfme_cred_repo']:
            print("You must supply a CFME Credentials REPO")
            ec += 1
        self.check_arg('selff', 'cfme/sel_ff_chrome')
        self.check_arg('gh_token', None)
        self.check_arg('gh_owner', None)
        self.check_arg('gh_repo', None)
        self.check_arg('gh_dev_repo', None)
        self.check_arg('gh_dev_owner', None)
        if self.args['dev_pr']:
            dev_check = [self.args[i] for i in ['gh_dev_repo', 'gh_dev_owner']]
            if not all(dev_check):
                print("To use dev_pr you must have a gh_dev_repo and gh_dev_owner defined")
                ec += 1
        self.check_arg('browser', 'firefox')
        self.check_arg('pytest', None)
        self.check_arg('pytest_con', 'py_test_base')
        if not self.args['pytest']:
            print("You must specify a py.test command")
            ec += 1
        self.check_arg('update_pip', False)
        self.check_arg('wheel_host_url', None)
        self.check_arg('auto_gen_test', False)
        self.check_arg('artifactor_dir', '/log_depot')
        self.check_arg('log_depot', None)
        if not self.args['log_depot']:
            print("You must specify a log_depot")
            ec += 1
        if self.args['pr'] and self.args['auto_gen_test'] and not \
           all([self.args['gh_token'], self.args['gh_owner'], self.args['gh_repo']]):
            print("You chose to use Auto Test Gen, without supplying GitHub details")
            ec += 1
        self.check_arg('capture', False)
        self.check_arg('test_id', fauxfactory.gen_alphanumeric(8))
        self.check_arg('prtester', False)
        self.check_arg('trackerbot', None)
        self.check_arg('wharf', False)
        self.check_arg('sprout_username', None)
        self.check_arg('sprout_password', None)
        self.check_arg('sprout_description', None)
        if ec:
            sys.exit(127)
    def display_banner(self):
        if self.args['banner']:
            banner = """
==================================================================
               ____             __             ____        __
     :        / __ \____  _____/ /_____  _____/ __ )____  / /_
   [* *]     / / / / __ \/ ___/ //_/ _ \/ ___/ __  / __ \/ __/
  -[___]-   / /_/ / /_/ / /__/ ,< /  __/ /  / /_/ / /_/ / /_
           /_____/\____/\___/_/|_|\___/_/  /_____/\____/\__/
==================================================================
            """
            print(banner)
    def process_appliance(self):
        self.appliance = self.args['appliance']
        self.app_name = self.args.get('appliance_name', "Unnamed")
        print("  APPLIANCE: {} ({})".format(self.appliance, self.app_name))
    def create_pytest_command(self):
        if self.args['auto_gen_test'] and self.args['pr']:
            self.pr_metadata = self.get_pr_metadata(self.args['pr'])
            pytest = self.pr_metadata.get('pytest', None)
            sprout_appliances = self.pr_metadata.get('sprouts', 1)
            if pytest:


Answer:                 self.args['pytest'] = "py.test {}".format(pytest)