diff options
Diffstat (limited to 'roles/lib_openshift/library/oc_obj.py')
-rw-r--r-- | roles/lib_openshift/library/oc_obj.py | 178 |
1 files changed, 99 insertions, 79 deletions
diff --git a/roles/lib_openshift/library/oc_obj.py b/roles/lib_openshift/library/oc_obj.py index 075c286e0..9b0c0e0e4 100644 --- a/roles/lib_openshift/library/oc_obj.py +++ b/roles/lib_openshift/library/oc_obj.py @@ -90,15 +90,15 @@ options: required: false default: str aliases: [] - all_namespace: + all_namespaces: description: - - The namespace where the object lives. + - Search in all namespaces for the object. required: false default: false aliases: [] kind: description: - - The kind attribute of the object. e.g. dc, bc, svc, route + - The kind attribute of the object. e.g. dc, bc, svc, route. May be a comma-separated list, e.g. "dc,po,svc". required: True default: None aliases: [] @@ -150,16 +150,16 @@ register: router_output # -*- -*- -*- Begin included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- -class YeditException(Exception): +class YeditException(Exception): # pragma: no cover ''' Exception class for Yedit ''' pass # pylint: disable=too-many-public-methods -class Yedit(object): +class Yedit(object): # pragma: no cover ''' Class to modify yaml files ''' re_valid_key = r"(((\[-?\d+\])|([0-9a-zA-Z%s/_-]+)).?)+$" - re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z%s/_-]+)" + re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z{}/_-]+)" com_sep = set(['.', '#', '|', ':']) # pylint: disable=too-many-arguments @@ -893,6 +893,13 @@ class OpenShiftCLI(object): def _replace(self, fname, force=False): '''replace the current object with oc replace''' + # We are removing the 'resourceVersion' to handle + # a race condition when modifying oc objects + yed = Yedit(fname) + results = yed.delete('metadata.resourceVersion') + if results[0]: + yed.write() + cmd = ['replace', '-f', fname] if force: cmd.append('--force') @@ -912,11 +919,15 @@ class OpenShiftCLI(object): '''call oc create on a filename''' return self.openshift_cmd(['create', '-f', fname]) - def _delete(self, resource, rname, selector=None): + def _delete(self, resource, name=None, selector=None): '''call oc delete on a resource''' - cmd = ['delete', resource, rname] - if selector: - cmd.append('--selector=%s' % selector) + cmd = ['delete', resource] + if selector is not None: + cmd.append('--selector={}'.format(selector)) + elif name is not None: + cmd.append(name) + else: + raise OpenShiftCLIError('Either name or selector is required when calling delete.') return self.openshift_cmd(cmd) @@ -934,7 +945,7 @@ class OpenShiftCLI(object): else: cmd.append(template_name) if params: - param_str = ["%s=%s" % (key, value) for key, value in params.items()] + param_str = ["{}={}".format(key, str(value).replace("'", r'"')) for key, value in params.items()] cmd.append('-v') cmd.extend(param_str) @@ -951,13 +962,13 @@ class OpenShiftCLI(object): return self.openshift_cmd(['create', '-f', fname]) - def _get(self, resource, rname=None, selector=None): + def _get(self, resource, name=None, selector=None): '''return a resource by name ''' cmd = ['get', resource] - if selector: - cmd.append('--selector=%s' % selector) - elif rname: - cmd.append(rname) + if selector is not None: + cmd.append('--selector={}'.format(selector)) + elif name is not None: + cmd.append(name) cmd.extend(['-o', 'json']) @@ -977,9 +988,9 @@ class OpenShiftCLI(object): if node: cmd.extend(node) else: - cmd.append('--selector=%s' % selector) + cmd.append('--selector={}'.format(selector)) - cmd.append('--schedulable=%s' % schedulable) + cmd.append('--schedulable={}'.format(schedulable)) return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') # noqa: E501 @@ -994,10 +1005,10 @@ class OpenShiftCLI(object): if node: cmd.extend(node) else: - cmd.append('--selector=%s' % selector) + cmd.append('--selector={}'.format(selector)) if pod_selector: - cmd.append('--pod-selector=%s' % pod_selector) + cmd.append('--pod-selector={}'.format(pod_selector)) cmd.extend(['--list-pods', '-o', 'json']) @@ -1010,16 +1021,16 @@ class OpenShiftCLI(object): if node: cmd.extend(node) else: - cmd.append('--selector=%s' % selector) + cmd.append('--selector={}'.format(selector)) if dry_run: cmd.append('--dry-run') if pod_selector: - cmd.append('--pod-selector=%s' % pod_selector) + cmd.append('--pod-selector={}'.format(pod_selector)) if grace_period: - cmd.append('--grace-period=%s' % int(grace_period)) + cmd.append('--grace-period={}'.format(int(grace_period))) if force: cmd.append('--force') @@ -1062,7 +1073,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout.decode(), stderr.decode() + return proc.returncode, stdout.decode('utf-8'), stderr.decode('utf-8') # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1079,10 +1090,6 @@ class OpenShiftCLI(object): elif self.namespace is not None and self.namespace.lower() not in ['none', 'emtpy']: # E501 cmds.extend(['-n', self.namespace]) - rval = {} - results = '' - err = None - if self.verbose: print(' '.join(cmds)) @@ -1092,39 +1099,31 @@ class OpenShiftCLI(object): returncode, stdout, stderr = 1, '', 'Failed to execute {}: {}'.format(subprocess.list2cmdline(cmds), ex) rval = {"returncode": returncode, - "results": results, "cmd": ' '.join(cmds)} - if returncode == 0: - if output: - if output_type == 'json': - try: - rval['results'] = json.loads(stdout) - except ValueError as verr: - if "No JSON object could be decoded" in verr.args: - err = verr.args - elif output_type == 'raw': - rval['results'] = stdout - - if self.verbose: - print("STDOUT: {0}".format(stdout)) - print("STDERR: {0}".format(stderr)) - - if err: - rval.update({"err": err, - "stderr": stderr, - "stdout": stdout, - "cmd": cmds}) + if output_type == 'json': + rval['results'] = {} + if output and stdout: + try: + rval['results'] = json.loads(stdout) + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + rval['err'] = verr.args + elif output_type == 'raw': + rval['results'] = stdout if output else '' + + if self.verbose: + print("STDOUT: {0}".format(stdout)) + print("STDERR: {0}".format(stderr)) - else: + if 'err' in rval or returncode != 0: rval.update({"stderr": stderr, - "stdout": stdout, - "results": {}}) + "stdout": stdout}) return rval -class Utils(object): +class Utils(object): # pragma: no cover ''' utilities for openshiftcli modules ''' @staticmethod @@ -1387,7 +1386,6 @@ class Utils(object): print('returning true') return True - class OpenShiftCLIConfig(object): '''Generic Config''' def __init__(self, rname, namespace, kubeconfig, options): @@ -1401,18 +1399,28 @@ class OpenShiftCLIConfig(object): ''' return config options ''' return self._options - def to_option_list(self): - '''return all options as a string''' - return self.stringify() - - def stringify(self): - ''' return the options hash as cli params in a string ''' + def to_option_list(self, ascommalist=''): + '''return all options as a string + if ascommalist is set to the name of a key, and + the value of that key is a dict, format the dict + as a list of comma delimited key=value pairs''' + return self.stringify(ascommalist) + + def stringify(self, ascommalist=''): + ''' return the options hash as cli params in a string + if ascommalist is set to the name of a key, and + the value of that key is a dict, format the dict + as a list of comma delimited key=value pairs ''' rval = [] for key in sorted(self.config_options.keys()): data = self.config_options[key] if data['include'] \ and (data['value'] or isinstance(data['value'], int)): - rval.append('--{}={}'.format(key.replace('_', '-'), data['value'])) + if key == ascommalist: + val = ','.join(['{}={}'.format(kk, vv) for kk, vv in sorted(data['value'].items())]) + else: + val = data['value'] + rval.append('--{}={}'.format(key.replace('_', '-'), val)) return rval @@ -1430,7 +1438,7 @@ class OCObject(OpenShiftCLI): def __init__(self, kind, namespace, - rname=None, + name=None, selector=None, kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False, @@ -1439,21 +1447,26 @@ class OCObject(OpenShiftCLI): super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose, all_namespaces=all_namespaces) self.kind = kind - self.name = rname + self.name = name self.selector = selector def get(self): '''return a kind by name ''' - results = self._get(self.kind, rname=self.name, selector=self.selector) - if results['returncode'] != 0 and 'stderr' in results and \ - '\"%s\" not found' % self.name in results['stderr']: + results = self._get(self.kind, name=self.name, selector=self.selector) + if (results['returncode'] != 0 and 'stderr' in results and + '\"{}\" not found'.format(self.name) in results['stderr']): results['returncode'] = 0 return results def delete(self): - '''return all pods ''' - return self._delete(self.kind, self.name) + '''delete the object''' + results = self._delete(self.kind, name=self.name, selector=self.selector) + if (results['returncode'] != 0 and 'stderr' in results and + '\"{}\" not found'.format(self.name) in results['stderr']): + results['returncode'] = 0 + + return results def create(self, files=None, content=None): ''' @@ -1529,24 +1542,31 @@ class OCObject(OpenShiftCLI): # Get ##### if state == 'list': - return {'changed': False, 'results': api_rval, 'state': 'list'} - - if not params['name']: - return {'failed': True, 'msg': 'Please specify a name when state is absent|present.'} # noqa: E501 + return {'changed': False, 'results': api_rval, 'state': state} ######## # Delete ######## if state == 'absent': - if not Utils.exists(api_rval['results'], params['name']): - return {'changed': False, 'state': 'absent'} + # verify its not in our results + if (params['name'] is not None or params['selector'] is not None) and \ + (len(api_rval['results']) == 0 or \ + ('items' in api_rval['results'][0] and len(api_rval['results'][0]['items']) == 0)): + return {'changed': False, 'state': state} if check_mode: return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete'} api_rval = ocobj.delete() - return {'changed': True, 'results': api_rval, 'state': 'absent'} + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + # create/update: Must define a name beyond this point + if not params['name']: + return {'failed': True, 'msg': 'Please specify a name when state is present.'} if state == 'present': ######## @@ -1572,7 +1592,7 @@ class OCObject(OpenShiftCLI): if params['files'] and params['delete_after']: Utils.cleanup(params['files']) - return {'changed': True, 'results': api_rval, 'state': "present"} + return {'changed': True, 'results': api_rval, 'state': state} ######## # Update @@ -1587,7 +1607,7 @@ class OCObject(OpenShiftCLI): if params['files'] and params['delete_after']: Utils.cleanup(params['files']) - return {'changed': False, 'results': api_rval['results'][0], 'state': "present"} + return {'changed': False, 'results': api_rval['results'][0], 'state': state} if check_mode: return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'} @@ -1606,7 +1626,7 @@ class OCObject(OpenShiftCLI): if api_rval['returncode'] != 0: return {'failed': True, 'msg': api_rval} - return {'changed': True, 'results': api_rval, 'state': "present"} + return {'changed': True, 'results': api_rval, 'state': state} # -*- -*- -*- End included fragment: class/oc_obj.py -*- -*- -*- @@ -1634,7 +1654,7 @@ def main(): force=dict(default=False, type='bool'), selector=dict(default=None, type='str'), ), - mutually_exclusive=[["content", "files"]], + mutually_exclusive=[["content", "files"], ["selector", "name"]], supports_check_mode=True, ) |