Bläddra i källkod

Merge pull request #1664 from kwoodson/api_dc

Deployment config module
Kenny Woodson 9 år sedan
förälder
incheckning
9c895c5e0a

+ 377 - 0
roles/lib_openshift_api/library/oc_deploymentconfig.py

@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+'''
+  OpenShiftCLI class that wraps the oc commands in a subprocess
+'''
+import atexit
+import json
+import os
+import shutil
+import subprocess
+import yaml
+
+class OpenShiftCLI(object):
+    ''' Class to wrap the oc command line tools '''
+    def __init__(self,
+                 namespace,
+                 kubeconfig='/etc/origin/master/admin.kubeconfig',
+                 verbose=False):
+        ''' Constructor for OpenshiftOC '''
+        self.namespace = namespace
+        self.verbose = verbose
+        self.kubeconfig = kubeconfig
+
+    def replace(self, fname, force=False):
+        '''return all pods '''
+        cmd = ['replace', '-f', fname]
+        if force:
+            cmd = ['replace', '--force', '-f', fname]
+        return self.oc_cmd(cmd)
+
+    def create(self, fname):
+        '''return all pods '''
+        return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
+
+    def delete(self, resource, rname):
+        '''return all pods '''
+        return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
+
+    def get(self, resource, rname=None):
+        '''return a secret by name '''
+        cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
+        if rname:
+            cmd.append(rname)
+
+        rval = self.oc_cmd(cmd, output=True)
+
+        # Ensure results are retuned in an array
+        if rval.has_key('items'):
+            rval['results'] = rval['items']
+        elif not isinstance(rval['results'], list):
+            rval['results'] = [rval['results']]
+
+        return rval
+
+    def oc_cmd(self, cmd, output=False):
+        '''Base command for oc '''
+        #cmds = ['/usr/bin/oc', '--config', self.kubeconfig]
+        cmds = ['/usr/bin/oc']
+        cmds.extend(cmd)
+
+        results = ''
+
+        if self.verbose:
+            print ' '.join(cmds)
+
+        proc = subprocess.Popen(cmds,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                env={'KUBECONFIG': self.kubeconfig})
+        proc.wait()
+        if proc.returncode == 0:
+            if output:
+                try:
+                    results = json.loads(proc.stdout.read())
+                except ValueError as err:
+                    if "No JSON object could be decoded" in err.message:
+                        results = err.message
+
+            if self.verbose:
+                print proc.stderr.read()
+                print results
+                print
+
+            return {"returncode": proc.returncode, "results": results}
+
+        return {"returncode": proc.returncode,
+                "stderr": proc.stderr.read(),
+                "stdout": proc.stdout.read(),
+                "results": {}
+               }
+
+class Utils(object):
+    ''' utilities for openshiftcli modules '''
+    @staticmethod
+    def create_file(rname, data, ftype=None):
+        ''' create a file in tmp with name and contents'''
+        path = os.path.join('/tmp', rname)
+        with open(path, 'w') as fds:
+            if ftype == 'yaml':
+                fds.write(yaml.dump(data, default_flow_style=False))
+
+            elif ftype == 'json':
+                fds.write(json.dumps(data))
+            else:
+                fds.write(data)
+
+        # Register cleanup when module is done
+        atexit.register(Utils.cleanup, [path])
+        return path
+
+    @staticmethod
+    def create_files_from_contents(data):
+        '''Turn an array of dict: filename, content into a files array'''
+        files = []
+
+        for sfile in data:
+            path = Utils.create_file(sfile['path'], sfile['content'])
+            files.append(path)
+
+        return files
+
+    @staticmethod
+    def cleanup(files):
+        '''Clean up on exit '''
+        for sfile in files:
+            if os.path.exists(sfile):
+                if os.path.isdir(sfile):
+                    shutil.rmtree(sfile)
+                elif os.path.isfile(sfile):
+                    os.remove(sfile)
+
+
+    @staticmethod
+    def exists(results, _name):
+        ''' Check to see if the results include the name '''
+        if not results:
+            return False
+
+
+        if Utils.find_result(results, _name):
+            return True
+
+        return False
+
+    @staticmethod
+    def find_result(results, _name):
+        ''' Find the specified result by name'''
+        rval = None
+        for result in results:
+            if result.has_key('metadata') and result['metadata']['name'] == _name:
+                rval = result
+                break
+
+        return rval
+
+    @staticmethod
+    def get_resource_file(sfile, sfile_type='yaml'):
+        ''' return the service file  '''
+        contents = None
+        with open(sfile) as sfd:
+            contents = sfd.read()
+
+        if sfile_type == 'yaml':
+            contents = yaml.load(contents)
+        elif sfile_type == 'json':
+            contents = json.loads(contents)
+
+        return contents
+
+    # Disabling too-many-branches.  This is a yaml dictionary comparison function
+    # pylint: disable=too-many-branches,too-many-return-statements
+    @staticmethod
+    def check_def_equal(user_def, result_def, debug=False):
+        ''' Given a user defined definition, compare it with the results given back by our query.  '''
+
+        # Currently these values are autogenerated and we do not need to check them
+        skip = ['creationTimestamp', 'selfLink', 'resourceVersion', 'uid', 'namespace']
+
+        for key, value in result_def.items():
+            if key in skip:
+                continue
+
+            # Both are lists
+            if isinstance(value, list):
+                if not isinstance(user_def[key], list):
+                    return False
+
+                # lists should be identical
+                if value != user_def[key]:
+                    return False
+
+            # recurse on a dictionary
+            elif isinstance(value, dict):
+                if not isinstance(user_def[key], dict):
+                    if debug:
+                        print "dict returned false not instance of dict"
+                    return False
+
+                # before passing ensure keys match
+                api_values = set(value.keys()) - set(skip)
+                user_values = set(user_def[key].keys()) - set(skip)
+                if api_values != user_values:
+                    if debug:
+                        print api_values
+                        print user_values
+                        print "keys are not equal in dict"
+                    return False
+
+                result = Utils.check_def_equal(user_def[key], value, debug=debug)
+                if not result:
+                    if debug:
+                        print "dict returned false"
+                    return False
+
+            # Verify each key, value pair is the same
+            else:
+                if not user_def.has_key(key) or value != user_def[key]:
+                    if debug:
+                        print "value not equal; user_def does not have key"
+                        print value
+                        print user_def[key]
+                    return False
+
+        return True
+
+class DeploymentConfig(OpenShiftCLI):
+    ''' Class to wrap the oc command line tools
+    '''
+    def __init__(self,
+                 namespace,
+                 dname=None,
+                 kubeconfig='/etc/origin/master/admin.kubeconfig',
+                 verbose=False):
+        ''' Constructor for OpenshiftOC '''
+        super(DeploymentConfig, self).__init__(namespace, kubeconfig)
+        self.namespace = namespace
+        self.name = dname
+        self.kubeconfig = kubeconfig
+        self.verbose = verbose
+
+    def get_dc(self):
+        '''return a deploymentconfig by name '''
+        return self.get('dc', self.name)
+
+    def delete_dc(self):
+        '''return all pods '''
+        return self.delete('dc', self.name)
+
+    def new_dc(self, dfile):
+        '''Create a deploymentconfig '''
+        return self.create(dfile)
+
+    def update_dc(self, dfile, force=False):
+        '''run update dc
+
+           This receives a list of file names and takes the first filename and calls replace.
+        '''
+        return self.replace(dfile, force)
+
+
+# pylint: disable=too-many-branches
+def main():
+    '''
+    ansible oc module for deploymentconfig
+    '''
+
+    module = AnsibleModule(
+        argument_spec=dict(
+            kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
+            state=dict(default='present', type='str',
+                       choices=['present', 'absent', 'list']),
+            debug=dict(default=False, type='bool'),
+            namespace=dict(default='default', type='str'),
+            name=dict(default=None, type='str'),
+            deploymentconfig_file=dict(default=None, type='str'),
+            input_type=dict(default='yaml', choices=['yaml', 'json'], type='str'),
+            delete_after=dict(default=False, type='bool'),
+            content=dict(default=None, type='dict'),
+            force=dict(default=False, type='bool'),
+        ),
+        mutually_exclusive=[["contents", "deploymentconfig_file"]],
+
+        supports_check_mode=True,
+    )
+    occmd = DeploymentConfig(module.params['namespace'],
+                             dname=module.params['name'],
+                             kubeconfig=module.params['kubeconfig'],
+                             verbose=module.params['debug'])
+
+    state = module.params['state']
+
+    api_rval = occmd.get_dc()
+
+    #####
+    # Get
+    #####
+    if state == 'list':
+        module.exit_json(changed=False, results=api_rval['results'], state="list")
+
+    if not module.params['name']:
+        module.fail_json(msg='Please specify a name when state is absent|present.')
+    ########
+    # Delete
+    ########
+    if state == 'absent':
+        if not Utils.exists(api_rval['results'], module.params['name']):
+            module.exit_json(changed=False, state="absent")
+
+        if module.check_mode:
+            module.exit_json(change=False, msg='Would have performed a delete.')
+
+        api_rval = occmd.delete_dc()
+        module.exit_json(changed=True, results=api_rval, state="absent")
+
+
+    if state == 'present':
+        if module.params['deploymentconfig_file']:
+            dfile = module.params['deploymentconfig_file']
+        elif module.params['content']:
+            dfile = Utils.create_file('dc', module.params['content'])
+        else:
+            module.fail_json(msg="Please specify content or deploymentconfig file.")
+
+        ########
+        # Create
+        ########
+        if not Utils.exists(api_rval['results'], module.params['name']):
+
+            if module.check_mode:
+                module.exit_json(change=False, msg='Would have performed a create.')
+
+            api_rval = occmd.new_dc(dfile)
+
+            # Remove files
+            if module.params['deploymentconfig_file'] and module.params['delete_after']:
+                Utils.cleanup([dfile])
+
+            if api_rval['returncode'] != 0:
+                module.fail_json(msg=api_rval)
+
+            module.exit_json(changed=True, results=api_rval, state="present")
+
+        ########
+        # Update
+        ########
+        if Utils.check_def_equal(Utils.get_resource_file(dfile), api_rval['results'][0]):
+
+            # Remove files
+            if module.params['deploymentconfig_file'] and module.params['delete_after']:
+                Utils.cleanup([dfile])
+
+            module.exit_json(changed=False, results=api_rval['results'], state="present")
+
+        if module.check_mode:
+            module.exit_json(change=False, msg='Would have performed an update.')
+
+        api_rval = occmd.update_dc(dfile, force=module.params['force'])
+
+        # Remove files
+        if module.params['deploymentconfig_file'] and module.params['delete_after']:
+            Utils.cleanup([dfile])
+
+        if api_rval['returncode'] != 0:
+            module.fail_json(msg=api_rval)
+
+
+        module.exit_json(changed=True, results=api_rval, state="present")
+
+    module.exit_json(failed=True,
+                     changed=False,
+                     results='Unknown state passed. %s' % state,
+                     state="unknown")
+
+# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
+# import module snippets.  This are required
+from ansible.module_utils.basic import *
+
+main()

+ 54 - 31
roles/lib_openshift_api/library/oc_secret.py

@@ -9,9 +9,6 @@ import shutil
 import subprocess
 import yaml
 
-# The base class is here to share methods.
-# Currently there is only 1 but will grow in the future.
-# pylint: disable=too-few-public-methods
 class OpenShiftCLI(object):
     ''' Class to wrap the oc command line tools '''
     def __init__(self,
@@ -23,6 +20,37 @@ class OpenShiftCLI(object):
         self.verbose = verbose
         self.kubeconfig = kubeconfig
 
+    def replace(self, fname, force=False):
+        '''return all pods '''
+        cmd = ['replace', '-f', fname]
+        if force:
+            cmd = ['replace', '--force', '-f', fname]
+        return self.oc_cmd(cmd)
+
+    def create(self, fname):
+        '''return all pods '''
+        return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
+
+    def delete(self, resource, rname):
+        '''return all pods '''
+        return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
+
+    def get(self, resource, rname=None):
+        '''return a secret by name '''
+        cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
+        if rname:
+            cmd.append(rname)
+
+        rval = self.oc_cmd(cmd, output=True)
+
+        # Ensure results are retuned in an array
+        if rval.has_key('items'):
+            rval['results'] = rval['items']
+        elif not isinstance(rval['results'], list):
+            rval['results'] = [rval['results']]
+
+        return rval
+
     def oc_cmd(self, cmd, output=False):
         '''Base command for oc '''
         #cmds = ['/usr/bin/oc', '--config', self.kubeconfig]
@@ -63,21 +91,33 @@ class OpenShiftCLI(object):
 class Utils(object):
     ''' utilities for openshiftcli modules '''
     @staticmethod
+    def create_file(rname, data, ftype=None):
+        ''' create a file in tmp with name and contents'''
+        path = os.path.join('/tmp', rname)
+        with open(path, 'w') as fds:
+            if ftype == 'yaml':
+                fds.write(yaml.dump(data, default_flow_style=False))
+
+            elif ftype == 'json':
+                fds.write(json.dumps(data))
+            else:
+                fds.write(data)
+
+        # Register cleanup when module is done
+        atexit.register(Utils.cleanup, [path])
+        return path
+
+    @staticmethod
     def create_files_from_contents(data):
         '''Turn an array of dict: filename, content into a files array'''
         files = []
 
         for sfile in data:
-            path = os.path.join('/tmp', sfile['path'])
-            with open(path, 'w') as fds:
-                fds.write(sfile['content'])
+            path = Utils.create_file(sfile['path'], sfile['content'])
             files.append(path)
 
-        # Register cleanup when module is done
-        atexit.register(Utils.cleanup, files)
         return files
 
-
     @staticmethod
     def cleanup(files):
         '''Clean up on exit '''
@@ -106,7 +146,6 @@ class Utils(object):
         ''' Find the specified result by name'''
         rval = None
         for result in results:
-            #print "%s == %s" % (result['metadata']['name'], name)
             if result.has_key('metadata') and result['metadata']['name'] == _name:
                 rval = result
                 break
@@ -166,7 +205,7 @@ class Utils(object):
                         print "keys are not equal in dict"
                     return False
 
-                result = Utils.check_def_equal(user_def[key], value)
+                result = Utils.check_def_equal(user_def[key], value, debug=debug)
                 if not result:
                     if debug:
                         print "dict returned false"
@@ -192,7 +231,7 @@ class Secret(OpenShiftCLI):
                  kubeconfig='/etc/origin/master/admin.kubeconfig',
                  verbose=False):
         ''' Constructor for OpenshiftOC '''
-        super(Secret, OpenShiftCLI).__init__(namespace, kubeconfig)
+        super(Secret, self).__init__(namespace, kubeconfig)
         self.namespace = namespace
         self.name = secret_name
         self.kubeconfig = kubeconfig
@@ -200,23 +239,11 @@ class Secret(OpenShiftCLI):
 
     def get_secrets(self):
         '''return a secret by name '''
-        cmd = ['get', 'secrets', '-o', 'json', '-n', self.namespace]
-        if self.name:
-            cmd.append(self.name)
-
-        rval = self.oc_cmd(cmd, output=True)
-
-        # Ensure results are retuned in an array
-        if rval.has_key('items'):
-            rval['results'] = rval['items']
-        elif not isinstance(rval['results'], list):
-            rval['results'] = [rval['results']]
-
-        return rval
+        return self.get('secrets', self.name)
 
     def delete_secret(self):
         '''return all pods '''
-        return self.oc_cmd(['delete', 'secrets', self.name, '-n', self.namespace])
+        return self.delete('secrets', self.name)
 
     def secret_new(self, files=None, contents=None):
         '''Create a secret with  all pods '''
@@ -243,13 +270,9 @@ class Secret(OpenShiftCLI):
         with open(sfile_path, 'w') as sfd:
             sfd.write(json.dumps(secret['results']))
 
-        cmd = ['replace', '-f', sfile_path]
-        if force:
-            cmd = ['replace', '--force', '-f', sfile_path]
-
         atexit.register(Utils.cleanup, [sfile_path])
 
-        return self.oc_cmd(cmd)
+        return self.replace(sfile_path, force=force)
 
     def prep_secret(self, files=None, contents=None):
         ''' return what the secret would look like if created

+ 59 - 37
roles/lib_openshift_api/library/oc_service.py

@@ -9,9 +9,6 @@ import shutil
 import subprocess
 import yaml
 
-# The base class is here to share methods.
-# Currently there is only 1 but will grow in the future.
-# pylint: disable=too-few-public-methods
 class OpenShiftCLI(object):
     ''' Class to wrap the oc command line tools '''
     def __init__(self,
@@ -23,6 +20,37 @@ class OpenShiftCLI(object):
         self.verbose = verbose
         self.kubeconfig = kubeconfig
 
+    def replace(self, fname, force=False):
+        '''return all pods '''
+        cmd = ['replace', '-f', fname]
+        if force:
+            cmd = ['replace', '--force', '-f', fname]
+        return self.oc_cmd(cmd)
+
+    def create(self, fname):
+        '''return all pods '''
+        return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
+
+    def delete(self, resource, rname):
+        '''return all pods '''
+        return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
+
+    def get(self, resource, rname=None):
+        '''return a secret by name '''
+        cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
+        if rname:
+            cmd.append(rname)
+
+        rval = self.oc_cmd(cmd, output=True)
+
+        # Ensure results are retuned in an array
+        if rval.has_key('items'):
+            rval['results'] = rval['items']
+        elif not isinstance(rval['results'], list):
+            rval['results'] = [rval['results']]
+
+        return rval
+
     def oc_cmd(self, cmd, output=False):
         '''Base command for oc '''
         #cmds = ['/usr/bin/oc', '--config', self.kubeconfig]
@@ -63,21 +91,33 @@ class OpenShiftCLI(object):
 class Utils(object):
     ''' utilities for openshiftcli modules '''
     @staticmethod
+    def create_file(rname, data, ftype=None):
+        ''' create a file in tmp with name and contents'''
+        path = os.path.join('/tmp', rname)
+        with open(path, 'w') as fds:
+            if ftype == 'yaml':
+                fds.write(yaml.dump(data, default_flow_style=False))
+
+            elif ftype == 'json':
+                fds.write(json.dumps(data))
+            else:
+                fds.write(data)
+
+        # Register cleanup when module is done
+        atexit.register(Utils.cleanup, [path])
+        return path
+
+    @staticmethod
     def create_files_from_contents(data):
         '''Turn an array of dict: filename, content into a files array'''
         files = []
 
         for sfile in data:
-            path = os.path.join('/tmp', sfile['path'])
-            with open(path, 'w') as fds:
-                fds.write(sfile['content'])
+            path = Utils.create_file(sfile['path'], sfile['content'])
             files.append(path)
 
-        # Register cleanup when module is done
-        atexit.register(Utils.cleanup, files)
         return files
 
-
     @staticmethod
     def cleanup(files):
         '''Clean up on exit '''
@@ -106,7 +146,6 @@ class Utils(object):
         ''' Find the specified result by name'''
         rval = None
         for result in results:
-            #print "%s == %s" % (result['metadata']['name'], name)
             if result.has_key('metadata') and result['metadata']['name'] == _name:
                 rval = result
                 break
@@ -166,7 +205,7 @@ class Utils(object):
                         print "keys are not equal in dict"
                     return False
 
-                result = Utils.check_def_equal(user_def[key], value)
+                result = Utils.check_def_equal(user_def[key], value, debug=debug)
                 if not result:
                     if debug:
                         print "dict returned false"
@@ -192,7 +231,7 @@ class Service(OpenShiftCLI):
                  kubeconfig='/etc/origin/master/admin.kubeconfig',
                  verbose=False):
         ''' Constructor for OpenshiftOC '''
-        super(Service, OpenShiftCLI).__init__(namespace, kubeconfig)
+        super(Service, self).__init__(namespace, kubeconfig)
         self.namespace = namespace
         self.name = service_name
         self.verbose = verbose
@@ -200,27 +239,15 @@ class Service(OpenShiftCLI):
 
     def create_service(self, sfile):
         ''' create the service '''
-        return self.oc_cmd(['create', '-f', sfile])
+        return self.create(sfile)
 
     def get_services(self):
         '''return a secret by name '''
-        cmd = ['get', 'services', '-o', 'json', '-n', self.namespace]
-        if self.name:
-            cmd.append(self.name)
-
-        rval = self.oc_cmd(cmd, output=True)
-
-        # Ensure results are retuned in an array
-        if rval.has_key('items'):
-            rval['results'] = rval['items']
-        elif not isinstance(rval['results'], list):
-            rval['results'] = [rval['results']]
-
-        return rval
+        return self.get('services', self.name)
 
     def delete_service(self):
         '''return all pods '''
-        return self.oc_cmd(['delete', 'service', self.name, '-n', self.namespace])
+        return self.delete('service', self.name)
 
     def update_service(self, sfile, force=False):
         '''run update service
@@ -228,14 +255,7 @@ class Service(OpenShiftCLI):
            This receives a list of file names and converts it into a secret.
            The secret is then written to disk and passed into the `oc replace` command.
         '''
-
-        cmd = ['replace', '-f', sfile]
-        if force:
-            cmd = ['replace', '--force', '-f', sfile]
-
-        atexit.register(Utils.cleanup, [sfile])
-
-        return self.oc_cmd(cmd)
+        return self.replace(sfile, force=force)
 
 
 # pylint: disable=too-many-branches
@@ -253,7 +273,9 @@ def main():
             namespace=dict(default='default', type='str'),
             name=dict(default=None, type='str'),
             service_file=dict(default=None, type='str'),
-            service_file_type=dict(default=None, type='str'),
+            input_type=dict(default='yaml',
+                            choices=['json', 'yaml'],
+                            type='str'),
             delete_after=dict(default=False, type='bool'),
             contents=dict(default=None, type='list'),
             force=dict(default=False, type='bool'),
@@ -320,7 +342,7 @@ def main():
         ########
         # Update
         ########
-        sfile_contents = Utils.get_resource_file(sfile, module.params['service_file_type'])
+        sfile_contents = Utils.get_resource_file(sfile, module.params['input_type'])
         if Utils.check_def_equal(sfile_contents, api_rval['results'][0]):
 
             # Remove files