#!/usr/local/sbin/charm-env python3
#
# For a usage examples, see README.md
#
# TODO
#
# - make the action idempotent (i.e. if you run it multiple times, the first
# run will create/delete the registry, and the reset will be a no-op and won't
# error out)
#
# - take only a plain authentication file, and create the encrypted version in
# the action
#
# - validate the parameters (make sure tlscert is a certificate, that tlskey is a
# proper key, etc)
#
# - when https://bugs.launchpad.net/juju/+bug/1661015 is fixed, handle the
# base64 encoding the parameters in the action itself

import os
import sys

from base64 import b64encode

from charmhelpers.core.hookenv import action_get
from charmhelpers.core.hookenv import action_set
from charms.templating.jinja2 import render
from subprocess import call, check_output

os.environ['PATH'] += os.pathsep + os.path.join(os.sep, 'snap', 'bin')

deletion = action_get('delete')

context = {}

arch = check_output(['dpkg', '--print-architecture']).rstrip()
context['arch'] = arch.decode('utf-8')

# These config options must be defined in the case of a creation
param_error = False
for param in ('tlscert', 'tlskey', 'domain', 'htpasswd', 'htpasswd-plain'):
    value = action_get(param)
    if not value and not deletion:
        key = "registry-create-parameter-{}".format(param)
        error = "failure, parameter {} is required".format(param)
        action_set({key: error})
        param_error = True

    context[param] = value

# Create the dockercfg template variable
dockercfg = '{"%s": {"auth": "%s", "email": "root@localhost"}}' % \
            (context['domain'], context['htpasswd-plain'])
context['dockercfg'] = b64encode(dockercfg.encode()).decode('ASCII')

if param_error:
    sys.exit(0)

# This one is either true or false, no need to check if it has a "good" value.
context['ingress'] = action_get('ingress')

# Declare a kubectl template when invoking kubectl
kubectl = ['kubectl', '--kubeconfig=/root/.kube/config']

# Remove deployment if requested
if deletion:
    resources = ['svc/kube-registry', 'rc/kube-registry-v0', 'secrets/registry-tls-data',
                 'secrets/registry-auth-data', 'secrets/registry-access']

    if action_get('ingress'):
        resources.append('ing/registry-ing')

    delete_command = kubectl + ['delete', '--ignore-not-found=true'] + resources
    delete_response = call(delete_command)
    if delete_response == 0:
        action_set({'registry-delete': 'success'})
    else:
        action_set({'registry-delete': 'failure'})

    sys.exit(0)

# Creation request
render('registry.yaml', '/root/cdk/addons/registry.yaml',
       context)

create_command = kubectl + ['create', '-f',
                            '/root/cdk/addons/registry.yaml']

create_response = call(create_command)

if create_response == 0:
    action_set({'registry-create': 'success'})

    # Create a ConfigMap if it doesn't exist yet, else patch it.
    # A ConfigMap is needed to change the default value for nginx' client_max_body_size.
    # The default is 1MB, and this is the maximum size of images that can be
    # pushed on the registry. 1MB images aren't useful, so we bump this value to 1024MB.
    cm_name = 'nginx-load-balancer-conf'
    check_cm_command = kubectl + ['get', 'cm', cm_name]
    check_cm_response = call(check_cm_command)

    if check_cm_response == 0:
        # There is an existing ConfigMap, patch it
        patch = '{"data":{"body-size":"1024m"}}'
        patch_cm_command = kubectl + ['patch', 'cm', cm_name, '-p', patch]
        patch_cm_response = call(patch_cm_command)

        if patch_cm_response == 0:
            action_set({'configmap-patch': 'success'})
        else:
            action_set({'configmap-patch': 'failure'})

    else:
        # No existing ConfigMap, create it
        render('registry-configmap.yaml', '/root/cdk/addons/registry-configmap.yaml',
               context)
        create_cm_command = kubectl + ['create', '-f', '/root/cdk/addons/registry-configmap.yaml']
        create_cm_response = call(create_cm_command)

        if create_cm_response == 0:
            action_set({'configmap-create': 'success'})
        else:
            action_set({'configmap-create': 'failure'})

    # Patch the "default" serviceaccount with an imagePullSecret.
    # This will allow the docker daemons to authenticate to our private
    # registry automatically
    patch = '{"imagePullSecrets":[{"name":"registry-access"}]}'
    patch_sa_command = kubectl + ['patch', 'sa', 'default', '-p', patch]
    patch_sa_response = call(patch_sa_command)

    if patch_sa_response == 0:
        action_set({'serviceaccount-patch': 'success'})
    else:
        action_set({'serviceaccount-patch': 'failure'})


else:
    action_set({'registry-create': 'failure'})
