From f55366615e4498987e44d8e370b5829c7a414168 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Tue, 5 Apr 2016 17:05:52 -0400 Subject: First attempt at oadm router module --- roles/lib_openshift_api/build/src/base.py | 65 ++++++++---- roles/lib_openshift_api/build/src/obj.py | 2 +- roles/lib_openshift_api/build/src/router.py | 152 ++++++++++++++++++++++++++++ roles/lib_openshift_api/build/src/secret.py | 4 +- 4 files changed, 201 insertions(+), 22 deletions(-) create mode 100644 roles/lib_openshift_api/build/src/router.py (limited to 'roles/lib_openshift_api/build/src') diff --git a/roles/lib_openshift_api/build/src/base.py b/roles/lib_openshift_api/build/src/base.py index 66831c4e2..257379d92 100644 --- a/roles/lib_openshift_api/build/src/base.py +++ b/roles/lib_openshift_api/build/src/base.py @@ -20,12 +20,12 @@ yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) # pylint: disable=too-few-public-methods class OpenShiftCLI(object): - ''' Class to wrap the oc command line tools ''' + ''' Class to wrap the command line tools ''' def __init__(self, namespace, kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False): - ''' Constructor for OpenshiftOC ''' + ''' Constructor for OpenshiftCLI ''' self.namespace = namespace self.verbose = verbose self.kubeconfig = kubeconfig @@ -58,15 +58,15 @@ class OpenShiftCLI(object): cmd = ['-n', self.namespace, 'replace', '-f', fname] if force: cmd.append('--force') - return self.oc_cmd(cmd) + return self.openshift_cmd(cmd) def _create(self, fname): '''return all pods ''' - return self.oc_cmd(['create', '-f', fname, '-n', self.namespace]) + return self.openshift_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]) + return self.openshift_cmd(['delete', resource, rname, '-n', self.namespace]) def _get(self, resource, rname=None): '''return a secret by name ''' @@ -74,7 +74,7 @@ class OpenShiftCLI(object): if rname: cmd.append(rname) - rval = self.oc_cmd(cmd, output=True) + rval = self.openshift_cmd(cmd, output=True) # Ensure results are retuned in an array if rval.has_key('items'): @@ -84,10 +84,15 @@ class OpenShiftCLI(object): return rval - def oc_cmd(self, cmd, output=False): + def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json'): '''Base command for oc ''' #cmds = ['/usr/bin/oc', '--config', self.kubeconfig] - cmds = ['/usr/bin/oc'] + cmds = [] + if oadm: + cmds = ['/usr/bin/oadm'] + else: + cmds = ['/usr/bin/oc'] + cmds.extend(cmd) rval = {} @@ -105,18 +110,21 @@ class OpenShiftCLI(object): proc.wait() stdout = proc.stdout.read() stderr = proc.stderr.read() - rval = {"returncode": proc.returncode, "results": results, + "cmd": ' '.join(cmds), } if proc.returncode == 0: if output: - try: - rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.message: - err = err.message + if output_type == 'json': + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.message: + err = err.message + elif output_type == 'raw': + rval['results'] = stdout if self.verbose: print stdout @@ -219,11 +227,13 @@ class Utils(object): # 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): + def check_def_equal(user_def, result_def, skip_keys=None, 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 = ['metadata', 'status'] + if skip_keys: + skip.extend(skip_keys) for key, value in result_def.items(): if key in skip: @@ -232,11 +242,27 @@ class Utils(object): # Both are lists if isinstance(value, list): if not isinstance(user_def[key], list): + if debug: + print 'user_def[key] is not a list' return False - # lists should be identical - if value != user_def[key]: - return False + for values in zip(user_def[key], value): + if isinstance(values[0], dict) and isinstance(values[1], dict): + if debug: + print 'sending list - list' + print type(values[0]) + print type(values[1]) + result = Utils.check_def_equal(values[0], values[1], skip_keys=skip_keys, debug=debug) + if not result: + print 'list compare returned false' + return False + + elif value != user_def[key]: + if debug: + print 'value should be identical' + print value + print user_def[key] + return False # recurse on a dictionary elif isinstance(value, dict): @@ -255,10 +281,11 @@ class Utils(object): print "keys are not equal in dict" return False - result = Utils.check_def_equal(user_def[key], value, debug=debug) + result = Utils.check_def_equal(user_def[key], value, skip_keys=skip_keys, debug=debug) if not result: if debug: print "dict returned false" + print result return False # Verify each key, value pair is the same diff --git a/roles/lib_openshift_api/build/src/obj.py b/roles/lib_openshift_api/build/src/obj.py index a3ad4b3c4..13aeba8e1 100644 --- a/roles/lib_openshift_api/build/src/obj.py +++ b/roles/lib_openshift_api/build/src/obj.py @@ -62,7 +62,7 @@ class OCObject(OpenShiftCLI): data = Utils.get_resource_file(files[0], content_type) # if equal then no need. So not equal is True - return not Utils.check_def_equal(data, objects['results'][0], True) + return not Utils.check_def_equal(data, objects['results'][0], skip_keys=None, debug=False) else: data = content diff --git a/roles/lib_openshift_api/build/src/router.py b/roles/lib_openshift_api/build/src/router.py new file mode 100644 index 000000000..69454d594 --- /dev/null +++ b/roles/lib_openshift_api/build/src/router.py @@ -0,0 +1,152 @@ +# pylint: skip-file + +import time + +class RouterConfig(object): + ''' RouterConfig is a DTO for the router. ''' + def __init__(self, rname, kubeconfig, router_options): + self.name = rname + self.kubeconfig = kubeconfig + self._router_options = router_options + + @property + def router_options(self): + ''' return router options ''' + return self._router_options + + def to_option_list(self): + ''' return all options as a string''' + return RouterConfig.stringify(self.router_options) + + @staticmethod + def stringify(options): + ''' return hash as list of key value pairs ''' + rval = [] + for key, data in options.items(): + if data['include'] and data['value']: + rval.append('--%s=%s' % (key.replace('_', '-'), data['value'])) + + return rval + +class Router(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + def __init__(self, + router_config, + verbose=False): + ''' Constructor for OpenshiftOC + + a router consists of 3 or more parts + - dc/router + - svc/router + - endpoint/router + ''' + super(Router, self).__init__('default', router_config.kubeconfig, verbose) + self.rconfig = router_config + self.verbose = verbose + self.router_parts = [{'kind': 'dc', 'name': self.rconfig.name}, + {'kind': 'svc', 'name': self.rconfig.name}, + #{'kind': 'endpoints', 'name': self.rconfig.name}, + ] + def get(self, filter_kind=None): + ''' return the self.router_parts ''' + rparts = self.router_parts + parts = [] + if filter_kind: + rparts = [part for part in self.router_parts if filter_kind == part['kind']] + + for part in rparts: + parts.append(self._get(part['kind'], rname=part['name'])) + + return parts + + def exists(self): + '''return a deploymentconfig by name ''' + parts = self.get() + for part in parts: + if part['returncode'] != 0: + return False + + return True + + def delete(self): + '''return all pods ''' + parts = [] + for part in self.router_parts: + parts.append(self._delete(part['kind'], part['name'])) + + return parts + + def create(self, dryrun=False, output=False, output_type='json'): + '''Create a deploymentconfig ''' + # We need to create the pem file + router_pem = '/tmp/router.pem' + with open(router_pem, 'w') as rfd: + rfd.write(open(self.rconfig.router_options['cert_file']['value']).read()) + rfd.write(open(self.rconfig.router_options['key_file']['value']).read()) + + atexit.register(Utils.cleanup, [router_pem]) + self.rconfig.router_options['default_cert']['value'] = router_pem + + options = self.rconfig.to_option_list() + + cmd = ['router'] + cmd.extend(options) + if dryrun: + cmd.extend(['--dry-run=True', '-o', 'json']) + + results = self.openshift_cmd(cmd, oadm=True, output=output, output_type=output_type) + + return results + + def update(self): + '''run update for the router. This performs a delete and then create ''' + parts = self.delete() + if any([part['returncode'] != 0 for part in parts]): + return parts + + # Ugly built in sleep here. + time.sleep(15) + + return self.create() + + def needs_update(self, verbose=False): + ''' check to see if we need to update ''' + dc_inmem = self.get(filter_kind='dc')[0] + if dc_inmem['returncode'] != 0: + return dc_inmem + + user_dc = self.create(dryrun=True, output=True, output_type='raw') + if user_dc['returncode'] != 0: + return user_dc + + # Since the output from oadm_router is returned as raw + # we need to parse it. The first line is the stats_password + user_dc_results = user_dc['results'].split('\n') + # stats_password = user_dc_results[0] + + # Load the string back into json and get the newly created dc + user_dc = json.loads('\n'.join(user_dc_results[1:]))['items'][0] + + # Router needs some exceptions. + # We do not want to check the autogenerated password for stats admin + if not self.rconfig.router_options['stats_password']['value']: + for idx, env_var in enumerate(user_dc['spec']['template']['spec']['containers'][0]['env']): + if env_var['name'] == 'STATS_PASSWORD': + env_var['value'] = \ + dc_inmem['results'][0]['spec']['template']['spec']['containers'][0]['env'][idx]['value'] + + # dry-run doesn't add the protocol to the ports section. We will manually do that. + for idx, port in enumerate(user_dc['spec']['template']['spec']['containers'][0]['ports']): + if not port.has_key('protocol'): + port['protocol'] = 'TCP' + + # These are different when generating + skip = ['dnsPolicy', + 'terminationGracePeriodSeconds', + 'restartPolicy', 'timeoutSeconds', + 'livenessProbe', 'readinessProbe', + 'terminationMessagePath', + 'rollingParams', + ] + + return not Utils.check_def_equal(user_dc, dc_inmem['results'][0], skip_keys=skip, debug=verbose) diff --git a/roles/lib_openshift_api/build/src/secret.py b/roles/lib_openshift_api/build/src/secret.py index af61dfa01..154716828 100644 --- a/roles/lib_openshift_api/build/src/secret.py +++ b/roles/lib_openshift_api/build/src/secret.py @@ -32,7 +32,7 @@ class Secret(OpenShiftCLI): cmd = ['-n%s' % self.namespace, 'secrets', 'new', self.name] cmd.extend(secrets) - return self.oc_cmd(cmd) + return self.openshift_cmd(cmd) def update(self, files, force=False): '''run update secret @@ -63,6 +63,6 @@ class Secret(OpenShiftCLI): cmd = ['-ojson', '-n%s' % self.namespace, 'secrets', 'new', self.name] cmd.extend(secrets) - return self.oc_cmd(cmd, output=True) + return self.openshift_cmd(cmd, output=True) -- cgit v1.2.3