#!/usr/bin/python
# -*- coding: utf-8 -*-

import argparse
import json
import os
import subprocess

CONF_FILE = '/etc/spotter/apps.json'
ISSUE_FILE = '/etc/issue'
NGINX_DIR = '/etc/nginx/conf.d'

NGINX_TEMPLATE = '''server {{
    listen [::]:{port} ssl http2;
    server_name {app}.{domain};

    access_log /var/log/nginx/{app}.access.log;
    error_log /var/log/nginx/{app}.error.log;

    location / {{
        proxy_pass http://{ip}:8080;
    }}
}}
'''

NGINX_DEFAULT_TEMPLATE = '''server {{
    listen [::]:80 default_server ipv6only=off;

    location / {{
        return 301 https://$host:{port}$request_uri;
    }}
    location /.well-known/acme-challenge/ {{
        root /etc/acme.sh.d;
    }}
}}

server {{
    listen [::]:{port} ssl http2 default_server ipv6only=off;
    root /srv/portal;
    index index.html;

    location / {{
        try_files $uri $uri/ =404;
    }}
    location /apps.json {{
        alias /etc/spotter/apps.json;
    }}
}}
'''

ISSUE_TEMPLATE = '''
\x1b[1;32m   _____             _   _         __      ____  __ 
  / ____|           | | | |        \\\\ \\\\    / /  \\\\/  |
 | (___  _ __   ___ | |_| |_ ___ _ _\\\\ \\\\  / /| \\\\  / |
  \\\\___ \\\\| '_ \\\\ / _ \\\\| __| __/ _ \\\\ '__\\\\ \\\\/ / | |\\\\/| |
  ____) | |_) | (_) | |_| ||  __/ |   \\\\  /  | |  | |
 |_____/| .__/ \\\\___/ \\\\__|\\\\__\\\\___|_|    \\\\/   |_|  |_|
        | |                                         
        |_|\x1b[0m




 \x1b[1;33mUPOZORNĚNÍ:\x1b[0m Neoprávněný přístup k tomuto zařízení je zakázán.
 Musíte mít výslovné oprávnění k přístupu nebo konfiguraci tohoto zařízení.
 Neoprávněné pokusy a kroky k přístupu nebo používání tohoto systému mohou mít
 za následek občanské nebo trestní sankce.


 \x1b[1;33mCAUTION:\x1b[0m Unauthozired access to this device is prohibited.
 You must have explicit, authorized permission to access or configure this
 device. Unauthorized attempts and actions to access or use this system may
 result in civil or criminal penalties.




 Pro přístup k aplikacím otevřete URL \x1b[1mhttps://{host}/\x1b[0m ve Vašem
 internetovém prohlížeči.






\x1b[0;30m
'''

class SpotterManager:
    def __init__(self):
        self.conf = {}
        with open(CONF_FILE, 'r') as f:
            self.conf = json.load(f)
        self.domain = self.conf["_"]["domain"]
        self.port = self.conf["_"]["port"]

    def save_conf(self):
        with open(CONF_FILE, 'w') as f:
            json.dump(self.conf, f)

    def add_app(self, app, args):
        self.add_app_to_conf(app, args)
        if args.url:
            self.update_app_conf(app)

    def add_app_to_conf(self, app, args):
        self.conf[app] = {}
        for key in ('url', 'login', 'password'):
            value = getattr(args, key)
            if value:
                self.conf[app][key] = value
        if args.property:
            for key, value in args.property:
                self.conf[app][key] = value
        self.save_conf()

    def update_app_conf(self, app):
        script_path = os.path.join('/srv', app, 'update-url.sh')
        if os.path.exists(script_path) and os.access(script_path, os.X_OK):
            host = '{}.{}'.format(app, self.domain)
            subprocess.call([script_path, host, self.port])
            subprocess.call(['service', app, 'restart'])

    def update_proxy(self, app):
        self.add_app_to_nginx(app)
        self.reload_nginx()

    def add_app_to_nginx(self, app):
        ip = get_container_ip(app)
        with open(os.path.join(NGINX_DIR, '{}.conf'.format(app)), 'w') as f:
            f.write(NGINX_TEMPLATE.format(app=app, ip=ip, domain=self.domain, port=self.port))

    def reload_nginx(self):
        subprocess.call(['service', 'nginx', 'reload'])

    def update_domain(self, domain, port):
        self.domain = self.conf["_"]["domain"] = domain
        self.port = self.conf["_"]["port"] = port
        self.save_conf()
        self.update_app_confs()
        self.rebuild_nginx()
        self.rebuild_issue()
        self.reload_nginx()

    def update_app_confs(self):
        for app in self.conf.iteritems():
            if 'url' in app[1]:
                self.update_app_conf(app[0])

    def rebuild_nginx(self):
        for f in os.listdir(NGINX_DIR):
            os.unlink(os.path.join(NGINX_DIR, f))
        with open(os.path.join(NGINX_DIR, 'default.conf'), 'w') as f:
            f.write(NGINX_DEFAULT_TEMPLATE.format(port=self.port))
        for app in self.conf.iteritems():
            if 'url' in app[1]:
                self.add_app_to_nginx(app[0])

    def rebuild_issue(self):
        host = self.domain
        if self.port != '443':
            host = '{}:{}'.format(host, self.port)
        with open(ISSUE_FILE, 'w') as f:
            f.write(ISSUE_TEMPLATE.format(host=host))

def get_container_ip(app):
    try:
        return subprocess.check_output(['docker', 'inspect', '-f', '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', app]).strip()
    except:
        return '127.0.0.1'

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Spotter VM application manager')
    subparsers = parser.add_subparsers()
    
    parser_add_app = subparsers.add_parser('add-app', help='Registers a new application')
    parser_add_app.set_defaults(action='add-app')
    parser_add_app.add_argument('app', help='Application name')
    parser_add_app.add_argument('url', nargs='?', help='URL to the application. Use "{host}" as a host placeholder')
    parser_add_app.add_argument('login', nargs='?', help='Administrative login')
    parser_add_app.add_argument('password', nargs='?', help='Administrative password')
    parser_add_app.add_argument('-p', '--property', nargs=2, action='append', help='Add arbitrary key-value to the application properties')

    parser_update_proxy = subparsers.add_parser('update-proxy', help='Updates nginx proxy target for an application container')
    parser_update_proxy.set_defaults(action='update-proxy')
    parser_update_proxy.add_argument('app', help='Application name')

    parser_update_domain = subparsers.add_parser('update-domain', help='Rebuilds domain structure of VM with new domain name and new HTTPS port')
    parser_update_domain.set_defaults(action='update-domain')
    parser_update_domain.add_argument('domain', help='Domain name')
    parser_update_domain.add_argument('port', help='HTTPS port')

    args = parser.parse_args()
    sm = SpotterManager()
    if args.action == 'add-app':
        sm.add_app(args.app, args)
    elif args.action == 'update-proxy':
        sm.update_proxy(args.app)
    elif args.action == 'update-domain':
        sm.update_domain(args.domain, args.port)
