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

DOCUMENTATION = """
---
module: kube
short_description: Manage Kubernetes Cluster
description:
  - Create, replace, remove, and stop resources within a Kubernetes Cluster
version_added: "2.0"
options:
  name:
    required: false
    default: null
    description:
      - The name associated with resource
  filename:
    required: false
    default: null
    description:
      - The path and filename of the resource(s) definition file.
  kubectl:
    required: false
    default: null
    description:
      - The path to the kubectl bin
  namespace:
    required: false
    default: null
    description:
      - The namespace associated with the resource(s)
  resource:
    required: false
    default: null
    description:
      - The resource to perform an action on. pods (po), replicationControllers (rc), services (svc)
  label:
    required: false
    default: null
    description:
      - The labels used to filter specific resources.
  server:
    required: false
    default: null
    description:
      - The url for the API server that commands are executed against.
  api_version:
    required: false
    choices: ['v1', 'v1beta3']
    default: v1
    description:
      - The API version associated with cluster.
  force:
    required: false
    default: false
    description:
      - A flag to indicate to force delete, replace, or stop.
  all:
    required: false
    default: false
    description:
      - A flag to indicate delete all, stop all, or all namespaces when checking exists.
  log_level:
    required: false
    default: 0
    description:
      - Indicates the level of verbosity of logging by kubectl.
  state:
    required: false
    choices: ['present', 'absent', 'latest', 'reloaded', 'stopped']
    default: present
    description:
      - present handles checking existence or creating if definition file provided,
        absent handles deleting resource(s) based on other options,
        latest handles creating ore updating based on existence,
        reloaded handles updating resource(s) definition using definition file,
        stopped handles stopping resource(s) based on other options.
requirements:
  - kubectl
author: "Kenny Jones (@kenjones-cisco)"
"""

EXAMPLES = """
- name: test nginx is present
  kube: name=nginx resource=rc state=present

- name: test nginx is stopped
  kube: name=nginx resource=rc state=stopped

- name: test nginx is absent
  kube: name=nginx resource=rc state=absent

- name: test nginx is present
  kube: filename=/tmp/nginx.yml
"""


class KubeManager(object):

    def __init__(self, module):

        self.module = module

        self.kubectl = module.params.get('kubectl')
        if self.kubectl is None:
            self.kubectl =  module.get_bin_path('kubectl', True)
        self.base_cmd = [self.kubectl]
        self.api_version = module.params.get('api_version')

        if self.api_version:
            self.base_cmd.append('--api-version=' + self.api_version)

        if module.params.get('server'):
            self.base_cmd.append('--server=' + module.params.get('server'))

        if module.params.get('log_level'):
            self.base_cmd.append('--v=' + str(module.params.get('log_level')))

        if module.params.get('namespace'):
            self.base_cmd.append('--namespace=' + module.params.get('namespace'))

        self.all = module.params.get('all')
        self.force = module.params.get('force')
        self.name = module.params.get('name')
        self.filename = module.params.get('filename')
        self.resource = module.params.get('resource')
        self.label = module.params.get('label')

    def _execute(self, cmd):
        args = self.base_cmd + cmd
        try:
            rc, out, err = self.module.run_command(args)
            if rc != 0:
                self.module.fail_json(
                    msg='error running kubectl (%s) command (rc=%d): %s' % (' '.join(args), rc, out or err))
        except Exception as exc:
            self.module.fail_json(
                msg='error running kubectl (%s) command: %s' % (' '.join(args), str(exc)))
        return out.splitlines()

    def _execute_nofail(self, cmd):
        args = self.base_cmd + cmd
        rc, out, err = self.module.run_command(args)
        if rc != 0:
            return None
        return out.splitlines()

    def create(self, check=True):
        if check and self.exists():
            return []

        cmd = ['create']

        if not self.filename:
            self.module.fail_json(msg='filename required to create')

        cmd.append('--filename=' + self.filename)

        return self._execute(cmd)

    def replace(self):

        if not self.force and not self.exists():
            return []

        cmd = ['replace']
        if self.api_version != 'v1':
            cmd = ['update']

        if self.force:
            cmd.append('--force')

        if not self.filename:
            self.module.fail_json(msg='filename required to reload')

        cmd.append('--filename=' + self.filename)

        return self._execute(cmd)

    def delete(self):

        if not self.force and not self.exists():
            return []

        cmd = ['delete']

        if self.filename:
            cmd.append('--filename=' + self.filename)
        else:
            if not self.resource:
                self.module.fail_json(msg='resource required to delete without filename')

            cmd.append(self.resource)

            if self.name:
                cmd.append(self.name)

            if self.label:
                cmd.append('--selector=' + self.label)

            if self.all:
                cmd.append('--all')

            if self.force:
                cmd.append('--ignore-not-found')

        return self._execute(cmd)

    def exists(self):
        cmd = ['get']

        if not self.resource:
            return False

        cmd.append(self.resource)

        if self.name:
            cmd.append(self.name)

        cmd.append('--no-headers')

        if self.label:
            cmd.append('--selector=' + self.label)

        if self.all:
            cmd.append('--all-namespaces')

        result = self._execute_nofail(cmd)
        if not result:
            return False
        return True

    def stop(self):

        if not self.force and not self.exists():
            return []

        cmd = ['stop']

        if self.filename:
            cmd.append('--filename=' + self.filename)
        else:
            if not self.resource:
                self.module.fail_json(msg='resource required to stop without filename')

            cmd.append(self.resource)

            if self.name:
                cmd.append(self.name)

            if self.label:
                cmd.append('--selector=' + self.label)

            if self.all:
                cmd.append('--all')

            if self.force:
                cmd.append('--ignore-not-found')

        return self._execute(cmd)


def main():

    module = AnsibleModule(
        argument_spec=dict(
            name=dict(),
            filename=dict(),
            namespace=dict(),
            resource=dict(),
            label=dict(),
            server=dict(),
            kubectl=dict(),
            api_version=dict(default='v1', choices=['v1', 'v1beta3']),
            force=dict(default=False, type='bool'),
            all=dict(default=False, type='bool'),
            log_level=dict(default=0, type='int'),
            state=dict(default='present', choices=['present', 'absent', 'latest', 'reloaded', 'stopped']),
            )
        )

    changed = False

    manager = KubeManager(module)
    state = module.params.get('state')

    if state == 'present':
        result = manager.create()

    elif state == 'absent':
        result = manager.delete()

    elif state == 'reloaded':
        result = manager.replace()

    elif state == 'stopped':
        result = manager.stop()

    elif state == 'latest':
        if manager.exists():
            manager.force = True
            result = manager.replace()
        else:
            result = manager.create(check=False)

    else:
        module.fail_json(msg='Unrecognized state %s.' % state)

    if result:
        changed = True
    module.exit_json(changed=changed,
                     msg='success: %s' % (' '.join(result))
                     )


from ansible.module_utils.basic import *  # noqa
if __name__ == '__main__':
    main()