#!/usr/bin/env python3
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import json
import logging
import os
import sys

import subprocess

CONF_FILE = os.environ.get('HEAT_SHELL_CONFIG',
                           '/var/run/heat-config/heat-config')

DOCKER_CMD = os.environ.get('HEAT_DOCKER_CMD', 'docker')


log = None


def main(argv=sys.argv):
    global log
    log = logging.getLogger('heat-config')
    handler = logging.StreamHandler(sys.stderr)
    handler.setFormatter(
        logging.Formatter(
            '[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s'))
    log.addHandler(handler)
    log.setLevel('DEBUG')

    if not os.path.exists(CONF_FILE):
        log.warning('No config file %s' % CONF_FILE)
        return 0

    try:
        configs = json.load(open(CONF_FILE))
    except ValueError as e:
        log.warning('Could not load config json: %s' % e)
        return 0

    cmd_config_ids = [c['id'] for c in configs
                      if c['group'] == 'docker-cmd']

    try:
        delete_missing_configs(cmd_config_ids)
    except Exception as e:
        log.exception(e)
    try:
        rename_containers()
    except Exception as e:
        log.exception(e)


def delete_missing_configs(config_ids):
    for conf_id in current_config_ids():
        if type(conf_id) is bytes:
            conf_id = conf_id.decode('utf-8')
        if conf_id not in config_ids:
            log.debug('%s no longer exists, deleting containers' % conf_id)
            remove_containers(conf_id)


def execute(cmd):
    log.debug("execute command: %s" % cmd)
    subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    cmd_stdout, cmd_stderr = subproc.communicate()
    return cmd_stdout, cmd_stderr, subproc.returncode


def current_config_ids():
    # List all config_id labels for containers managed by docker-cmd
    cmd = [
        DOCKER_CMD, 'ps', '-a',
        '--filter', 'label=managed_by=docker-cmd',
        '--format', '{{.Label "config_id"}}'
    ]
    cmd_stdout, cmd_stderr, returncode = execute(cmd)
    if returncode != 0:
        return set()
    return set(cmd_stdout.split())


def remove_containers(conf_id):
    cmd = [
        DOCKER_CMD, 'ps', '-q', '-a',
        '--filter', 'label=managed_by=docker-cmd',
        '--filter', 'label=config_id=%s' % conf_id
    ]
    cmd_stdout, cmd_stderr, returncode = execute(cmd)
    if returncode == 0:
        for container in cmd_stdout.split():
            remove_container(container)


def remove_container(container):
    cmd = [DOCKER_CMD, 'rm', '-f', container]
    cmd_stdout, cmd_stderr, returncode = execute(cmd)
    if returncode != 0:
        log.error('Error removing container: %s' % container)
        log.error(cmd_stderr)


def rename_containers():
    # list every container name, and its container_name label
    cmd = [
        DOCKER_CMD, 'ps', '-a',
        '--format', '{{.Names}} {{.Label "container_name"}}'
    ]
    cmd_stdout, cmd_stderr, returncode = execute(cmd)
    if returncode != 0:
        return

    lines = cmd_stdout.split(b"\n")
    current_containers = []
    need_renaming = {}
    for line in lines:
        entry = line.split()
        if not entry:
            continue
        current_containers.append(entry[0])

        # ignore if container_name label not set
        if len(entry) < 2:
            continue

        # ignore if desired name is already actual name
        if entry[0] == entry[-1]:
            continue

        need_renaming[entry[0]] = entry[-1]

    for current, desired in sorted(need_renaming.items()):
        if desired in current_containers:
            log.info('Cannot rename "%s" since "%s" still exists' % (
                current, desired))
        else:
            cmd = [DOCKER_CMD, 'rename', current, desired]
            execute(cmd)


if __name__ == '__main__':
    sys.exit(main(sys.argv))
