#!/usr/bin/env python3
"""
Script to install jupyterhub helm chart with minikube

for testing binderhub

Gets the jupyterhub chart version from the binderhub helm chart
to ensure we are testing against a reasonable version.
"""
import sys
import os
import pipes
from subprocess import check_call, check_output
import time
import socket

from kubernetes import client, config
from ruamel import yaml

auth_enabled = '--auth' in sys.argv

namespace = os.environ.get('BINDER_TEST_NAMESPACE') or 'binder-test'
name = 'binder-test-hub'

here = os.path.abspath(os.path.dirname(__file__))
helm_chart = os.path.join(here, os.pardir, os.pardir, 'helm-chart')
requirements_yaml = os.path.join(helm_chart, 'binderhub', 'requirements.yaml')

def get_hub_chart_dependency():
    """Get the JupyterHub chart info from the binderhub chart requirements.yaml"""
    with open(requirements_yaml) as f:
        requirements = yaml.safe_load(f)
    for dep in requirements['dependencies']:
        if dep['name'] == 'jupyterhub':
            return dep
    else:
        raise ValueError("Couldn't find JupyterHub in %s:\n%s" %
            (requirements_yaml, requirements)
        )

jupyterhub = get_hub_chart_dependency()

# update the helm repo
check_call(['helm', 'repo', 'add', 'jupyterhub', jupyterhub['repository']])
check_call(['helm', 'repo', 'update', 'jupyterhub'])

# launch with helm install (or upgrade, if already installed)
args = [
    'jupyterhub/jupyterhub',
    f'--version={jupyterhub["version"]}',
    f'--namespace={namespace}',
    '-f', os.path.join(here, 'jupyterhub-helm-config.yaml'),
]
if auth_enabled:
    print('\nAuthentication is enabled')
    auth_conf_file = os.path.join(here, 'jupyterhub-helm-auth-config.yaml')
    args.extend(['-f', auth_conf_file])

    # get host IP (https://stackoverflow.com/a/28950776/4361882)
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    print('Host IP: {}'.format(IP))
    # insert host IP
    with open(auth_conf_file, 'r') as file:
        filedata = file.read()
    filedata = filedata.replace('<host_ip>', IP)
    with open(auth_conf_file, 'w') as file:
        file.write(filedata)

is_running = name in check_output(['helm', 'list', '-q']).decode('utf8', 'replace').split()
if is_running:
    cmd = ['helm', 'upgrade', name]
else:
    cmd = ['helm', 'install', f'--name={name}']

cmd.extend(args)
print("\n    %s\n" % ' '.join(map(pipes.quote, cmd)))

check_call(cmd)


# wait for deployment to be running via kube API

config.load_kube_config()
kube = client.CoreV1Api()

def wait_for(f, msg, timeout=300):
    """Wait for f() to return True"""
    print(f"Waiting until {msg}")
    for i in range(int(timeout)):
        if f():
            break
        time.sleep(1)
    else:
        raise TimeoutError(f"{msg} did not occur in {timeout} seconds")
    print(msg)

def hub_running():
    """Return whether all pods in our test namespace are ready"""
    pods = kube.list_namespaced_pod(namespace).items
    # debug:
    for pod in pods:
        print(f"{pod.status.phase}\t{pod.metadata.name}")
    return all(pod.status.phase == 'Running' for pod in pods)

# wait until all of our pods are running
try:
    wait_for(hub_running, "Hub is up")
except TimeoutError:
    # show pods on timeout, in case there's a hint about what's wrong
    check_call(['kubectl', 'get', 'pod', '--all-namespaces'])
    raise
