diff options
Diffstat (limited to 'roles/lib_openshift/src/lib')
-rw-r--r-- | roles/lib_openshift/src/lib/base.py | 116 | ||||
-rw-r--r-- | roles/lib_openshift/src/lib/clusterrole.py | 68 | ||||
-rw-r--r-- | roles/lib_openshift/src/lib/rule.py | 144 | ||||
-rw-r--r-- | roles/lib_openshift/src/lib/secret.py | 5 | ||||
-rw-r--r-- | roles/lib_openshift/src/lib/service.py | 61 |
5 files changed, 336 insertions, 58 deletions
diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py index 132c586c9..16770b22d 100644 --- a/roles/lib_openshift/src/lib/base.py +++ b/roles/lib_openshift/src/lib/base.py @@ -76,6 +76,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') @@ -95,11 +102,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) @@ -117,7 +128,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) @@ -134,13 +145,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']) @@ -160,9 +171,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 @@ -177,10 +188,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']) @@ -193,16 +204,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') @@ -245,7 +256,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): @@ -262,10 +273,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)) @@ -275,34 +282,26 @@ 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 '' - else: + if self.verbose: + print("STDOUT: {0}".format(stdout)) + print("STDERR: {0}".format(stderr)) + + if 'err' in rval or returncode != 0: rval.update({"stderr": stderr, - "stdout": stdout, - "results": {}}) + "stdout": stdout}) return rval @@ -570,7 +569,6 @@ class Utils(object): print('returning true') return True - class OpenShiftCLIConfig(object): '''Generic Config''' def __init__(self, rname, namespace, kubeconfig, options): @@ -584,18 +582,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 diff --git a/roles/lib_openshift/src/lib/clusterrole.py b/roles/lib_openshift/src/lib/clusterrole.py new file mode 100644 index 000000000..93ffababf --- /dev/null +++ b/roles/lib_openshift/src/lib/clusterrole.py @@ -0,0 +1,68 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-public-methods +class ClusterRole(Yedit): + ''' Class to model an openshift ClusterRole''' + rules_path = "rules" + + def __init__(self, name=None, content=None): + ''' Constructor for clusterrole ''' + if content is None: + content = ClusterRole.builder(name).yaml_dict + + super(ClusterRole, self).__init__(content=content) + + self.__rules = Rule.parse_rules(self.get(ClusterRole.rules_path)) or [] + + @property + def rules(self): + return self.__rules + + @rules.setter + def rules(self, data): + self.__rules = data + self.put(ClusterRole.rules_path, self.__rules) + + def rule_exists(self, inc_rule): + '''attempt to find the inc_rule in the rules list''' + for rule in self.rules: + if rule == inc_rule: + return True + + return False + + def compare(self, other, verbose=False): + '''compare function for clusterrole''' + for rule in other.rules: + if rule not in self.rules: + if verbose: + print('Rule in other not found in self. [{}]'.format(rule)) + return False + + for rule in self.rules: + if rule not in other.rules: + if verbose: + print('Rule in self not found in other. [{}]'.format(rule)) + return False + + return True + + @staticmethod + def builder(name='default_clusterrole', rules=None): + '''return a clusterrole with name and/or rules''' + if rules is None: + rules = [{'apiGroups': [""], + 'attributeRestrictions': None, + 'verbs': [], + 'resources': []}] + content = { + 'apiVersion': 'v1', + 'kind': 'ClusterRole', + 'metadata': {'name': '{}'.format(name)}, + 'rules': rules, + } + + return ClusterRole(content=content) + diff --git a/roles/lib_openshift/src/lib/rule.py b/roles/lib_openshift/src/lib/rule.py new file mode 100644 index 000000000..fe5ed9723 --- /dev/null +++ b/roles/lib_openshift/src/lib/rule.py @@ -0,0 +1,144 @@ +# pylint: skip-file +# flake8: noqa + + +class Rule(object): + '''class to represent a clusterrole rule + + Example Rule Object's yaml: + - apiGroups: + - "" + attributeRestrictions: null + resources: + - persistentvolumes + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + + ''' + def __init__(self, + api_groups=None, + attr_restrictions=None, + resources=None, + verbs=None): + self.__api_groups = api_groups if api_groups is not None else [""] + self.__verbs = verbs if verbs is not None else [] + self.__resources = resources if resources is not None else [] + self.__attribute_restrictions = attr_restrictions if attr_restrictions is not None else None + + @property + def verbs(self): + '''property for verbs''' + if self.__verbs is None: + return [] + + return self.__verbs + + @verbs.setter + def verbs(self, data): + '''setter for verbs''' + self.__verbs = data + + @property + def api_groups(self): + '''property for api_groups''' + if self.__api_groups is None: + return [] + return self.__api_groups + + @api_groups.setter + def api_groups(self, data): + '''setter for api_groups''' + self.__api_groups = data + + @property + def resources(self): + '''property for resources''' + if self.__resources is None: + return [] + + return self.__resources + + @resources.setter + def resources(self, data): + '''setter for resources''' + self.__resources = data + + @property + def attribute_restrictions(self): + '''property for attribute_restrictions''' + return self.__attribute_restrictions + + @attribute_restrictions.setter + def attribute_restrictions(self, data): + '''setter for attribute_restrictions''' + self.__attribute_restrictions = data + + def add_verb(self, inc_verb): + '''add a verb to the verbs array''' + self.verbs.append(inc_verb) + + def add_api_group(self, inc_apigroup): + '''add an api_group to the api_groups array''' + self.api_groups.append(inc_apigroup) + + def add_resource(self, inc_resource): + '''add an resource to the resources array''' + self.resources.append(inc_resource) + + def remove_verb(self, inc_verb): + '''add a verb to the verbs array''' + try: + self.verbs.remove(inc_verb) + return True + except ValueError: + pass + + return False + + def remove_api_group(self, inc_api_group): + '''add a verb to the verbs array''' + try: + self.api_groups.remove(inc_api_group) + return True + except ValueError: + pass + + return False + + def remove_resource(self, inc_resource): + '''add a verb to the verbs array''' + try: + self.resources.remove(inc_resource) + return True + except ValueError: + pass + + return False + + def __eq__(self, other): + '''return whether rules are equal''' + return (self.attribute_restrictions == other.attribute_restrictions and + self.api_groups == other.api_groups and + self.resources == other.resources and + self.verbs == other.verbs) + + + @staticmethod + def parse_rules(inc_rules): + '''create rules from an array''' + + results = [] + for rule in inc_rules: + results.append(Rule(rule.get('apiGroups', ['']), + rule.get('attributeRestrictions', None), + rule.get('resources', []), + rule.get('verbs', []))) + + return results diff --git a/roles/lib_openshift/src/lib/secret.py b/roles/lib_openshift/src/lib/secret.py index 75c32e8b1..a1c202442 100644 --- a/roles/lib_openshift/src/lib/secret.py +++ b/roles/lib_openshift/src/lib/secret.py @@ -9,10 +9,12 @@ class SecretConfig(object): sname, namespace, kubeconfig, - secrets=None): + secrets=None, + stype=None): ''' constructor for handling secret options ''' self.kubeconfig = kubeconfig self.name = sname + self.type = stype self.namespace = namespace self.secrets = secrets self.data = {} @@ -23,6 +25,7 @@ class SecretConfig(object): ''' assign the correct properties for a secret dict ''' self.data['apiVersion'] = 'v1' self.data['kind'] = 'Secret' + self.data['type'] = self.type self.data['metadata'] = {} self.data['metadata']['name'] = self.name self.data['metadata']['namespace'] = self.namespace diff --git a/roles/lib_openshift/src/lib/service.py b/roles/lib_openshift/src/lib/service.py index eef568779..0e8cc3aa5 100644 --- a/roles/lib_openshift/src/lib/service.py +++ b/roles/lib_openshift/src/lib/service.py @@ -15,7 +15,8 @@ class ServiceConfig(object): cluster_ip=None, portal_ip=None, session_affinity=None, - service_type=None): + service_type=None, + external_ips=None): ''' constructor for handling service options ''' self.name = sname self.namespace = namespace @@ -26,6 +27,7 @@ class ServiceConfig(object): self.portal_ip = portal_ip self.session_affinity = session_affinity self.service_type = service_type + self.external_ips = external_ips self.data = {} self.create_dict() @@ -38,8 +40,9 @@ class ServiceConfig(object): self.data['metadata']['name'] = self.name self.data['metadata']['namespace'] = self.namespace if self.labels: - for lab, lab_value in self.labels.items(): - self.data['metadata'][lab] = lab_value + self.data['metadata']['labels'] = {} + for lab, lab_value in self.labels.items(): + self.data['metadata']['labels'][lab] = lab_value self.data['spec'] = {} if self.ports: @@ -61,6 +64,10 @@ class ServiceConfig(object): if self.service_type: self.data['spec']['type'] = self.service_type + if self.external_ips: + self.data['spec']['externalIPs'] = self.external_ips + + # pylint: disable=too-many-instance-attributes,too-many-public-methods class Service(Yedit): ''' Class to model the oc service object ''' @@ -69,6 +76,7 @@ class Service(Yedit): cluster_ip = "spec.clusterIP" selector_path = 'spec.selector' kind = 'Service' + external_ips = "spec.externalIPs" def __init__(self, content): '''Service constructor''' @@ -129,3 +137,50 @@ class Service(Yedit): def add_portal_ip(self, pip): '''add cluster ip''' self.put(Service.portal_ip, pip) + + def get_external_ips(self): + ''' get a list of external_ips ''' + return self.get(Service.external_ips) or [] + + def add_external_ips(self, inc_external_ips): + ''' add an external_ip to the external_ips list ''' + if not isinstance(inc_external_ips, list): + inc_external_ips = [inc_external_ips] + + external_ips = self.get_external_ips() + if not external_ips: + self.put(Service.external_ips, inc_external_ips) + else: + external_ips.extend(inc_external_ips) + + return True + + def find_external_ips(self, inc_external_ip): + ''' find a specific external IP ''' + val = None + try: + idx = self.get_external_ips().index(inc_external_ip) + val = self.get_external_ips()[idx] + except ValueError: + pass + + return val + + def delete_external_ips(self, inc_external_ips): + ''' remove an external IP from a service ''' + if not isinstance(inc_external_ips, list): + inc_external_ips = [inc_external_ips] + + external_ips = self.get(Service.external_ips) or [] + + if not external_ips: + return True + + removed = False + for inc_external_ip in inc_external_ips: + external_ip = self.find_external_ips(inc_external_ip) + if external_ip: + external_ips.remove(external_ip) + removed = True + + return removed |