diff options
Diffstat (limited to 'roles/lib_openshift/src')
| -rw-r--r-- | roles/lib_openshift/src/ansible/oc_clusterrole.py | 29 | ||||
| -rw-r--r-- | roles/lib_openshift/src/class/oc_clusterrole.py | 163 | ||||
| -rw-r--r-- | roles/lib_openshift/src/doc/clusterrole | 66 | ||||
| -rw-r--r-- | roles/lib_openshift/src/lib/clusterrole.py | 68 | ||||
| -rw-r--r-- | roles/lib_openshift/src/lib/rule.py | 143 | ||||
| -rw-r--r-- | roles/lib_openshift/src/sources.yml | 12 | ||||
| -rwxr-xr-x | roles/lib_openshift/src/test/integration/oc_clusterrole.yml | 106 | ||||
| -rwxr-xr-x | roles/lib_openshift/src/test/unit/test_oc_clusterrole.py | 116 | 
8 files changed, 703 insertions, 0 deletions
| diff --git a/roles/lib_openshift/src/ansible/oc_clusterrole.py b/roles/lib_openshift/src/ansible/oc_clusterrole.py new file mode 100644 index 000000000..7e4319d2c --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_clusterrole.py @@ -0,0 +1,29 @@ +# pylint: skip-file +# flake8: noqa + +def main(): +    ''' +    ansible oc module for clusterrole +    ''' + +    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'), +            name=dict(default=None, type='str'), +            rules=dict(default=None, type='list'), +        ), +        supports_check_mode=True, +    ) + +    results = OCClusterRole.run_ansible(module.params, module.check_mode) + +    if 'failed' in results: +        module.fail_json(**results) + +    module.exit_json(**results) + +if __name__ == '__main__': +    main() diff --git a/roles/lib_openshift/src/class/oc_clusterrole.py b/roles/lib_openshift/src/class/oc_clusterrole.py new file mode 100644 index 000000000..0d8ed42ba --- /dev/null +++ b/roles/lib_openshift/src/class/oc_clusterrole.py @@ -0,0 +1,163 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCClusterRole(OpenShiftCLI): +    ''' Class to manage clusterrole objects''' +    kind = 'clusterrole' + +    def __init__(self, +                 name, +                 rules=None, +                 kubeconfig=None, +                 verbose=False): +        ''' Constructor for OCClusterRole ''' +        super(OCClusterRole, self).__init__(None, kubeconfig=kubeconfig, verbose=verbose) +        self.verbose = verbose +        self.name = name +        self._clusterrole = None +        self._inc_clusterrole = ClusterRole.builder(name, rules) + +    @property +    def clusterrole(self): +        ''' property for clusterrole''' +        if not self._clusterrole: +            self.get() +        return self._clusterrole + +    @clusterrole.setter +    def clusterrole(self, data): +        ''' setter function for clusterrole property''' +        self._clusterrole = data + +    @property +    def inc_clusterrole(self): +        ''' property for inc_clusterrole''' +        return self._inc_clusterrole + +    @inc_clusterrole.setter +    def inc_clusterrole(self, data): +        ''' setter function for inc_clusterrole property''' +        self._inc_clusterrole = data + +    def exists(self): +        ''' return whether a clusterrole exists ''' +        if self.clusterrole: +            return True + +        return False + +    def get(self): +        '''return a clusterrole ''' +        result = self._get(self.kind, self.name) + +        if result['returncode'] == 0: +            self.clusterrole = ClusterRole(content=result['results'][0]) +            result['results'] = self.clusterrole.yaml_dict + +        elif 'clusterrole "{}" not found'.format(self.name) in result['stderr']: +            result['returncode'] = 0 + +        return result + +    def delete(self): +        '''delete the object''' +        return self._delete(self.kind, self.name) + +    def create(self): +        '''create a clusterrole from the proposed incoming clusterrole''' +        return self._create_from_content(self.name, self.inc_clusterrole.yaml_dict) + +    def update(self): +        '''update a project''' +        return self._replace_content(self.kind, self.name, self.inc_clusterrole.yaml_dict) + +    def needs_update(self): +        ''' verify an update is needed''' +        return not self.clusterrole.compare(self.inc_clusterrole, self.verbose) + +    # pylint: disable=too-many-return-statements,too-many-branches +    @staticmethod +    def run_ansible(params, check_mode): +        '''run the idempotent ansible code''' + +        oc_clusterrole  = OCClusterRole(params['name'], +                                        params['rules'], +                                        params['kubeconfig'], +                                        params['debug']) + +        state = params['state'] + +        api_rval = oc_clusterrole.get() + +        ##### +        # Get +        ##### +        if state == 'list': +            return {'changed': False, 'results': api_rval, 'state': state} + +        ######## +        # Delete +        ######## +        if state == 'absent': +            if oc_clusterrole.exists(): + +                if check_mode: +                    return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'} + +                api_rval = oc_clusterrole.delete() + +                if api_rval['returncode'] != 0: +                    return {'failed': True, 'msg': api_rval} + +                return {'changed': True, 'results': api_rval, 'state': state} + +            return {'changed': False, 'state': state} + +        if state == 'present': +            ######## +            # Create +            ######## +            if not oc_clusterrole.exists(): + +                if check_mode: +                    return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + +                # Create it here +                api_rval = oc_clusterrole.create() + +                # return the created object +                api_rval = oc_clusterrole.get() + +                if api_rval['returncode'] != 0: +                    return {'failed': True, 'msg': api_rval} + +                return {'changed': True, 'results': api_rval, 'state': state} + +            ######## +            # Update +            ######## +            if oc_clusterrole.needs_update(): + +                if check_mode: +                    return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'} + +                api_rval = oc_clusterrole.update() + +                if api_rval['returncode'] != 0: +                    return {'failed': True, 'msg': api_rval} + +                # return the created object +                api_rval = oc_clusterrole.get() + +                if api_rval['returncode'] != 0: +                    return {'failed': True, 'msg': api_rval} + +                return {'changed': True, 'results': api_rval, 'state': state} + +            return {'changed': False, 'results': api_rval, 'state': state} + +        return {'failed': True, +                'changed': False, +                'msg': 'Unknown state passed. [%s]' % state} diff --git a/roles/lib_openshift/src/doc/clusterrole b/roles/lib_openshift/src/doc/clusterrole new file mode 100644 index 000000000..3d14a2dfb --- /dev/null +++ b/roles/lib_openshift/src/doc/clusterrole @@ -0,0 +1,66 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_clusterrole +short_description: Modify, and idempotently manage openshift clusterroles +description: +  - Manage openshift clusterroles +options: +  state: +    description: +    - Supported states, present, absent, list +    - present - will ensure object is created or updated to the value specified +    - list - will return a clusterrole +    - absent - will remove a clusterrole +    required: False +    default: present +    choices: ["present", 'absent', 'list'] +    aliases: [] +  kubeconfig: +    description: +    - The path for the kubeconfig file to use for authentication +    required: false +    default: /etc/origin/master/admin.kubeconfig +    aliases: [] +  debug: +    description: +    - Turn on debug output. +    required: false +    default: False +    aliases: [] +  name: +    description: +    - Name of the object that is being queried. +    required: false +    default: None +    aliases: [] +  rules: +    description: +    - A list of dictionaries that have the rule parameters. +    - e.g. rules=[{'apiGroups': [""], 'attributeRestrictions': None, 'verbs': ['get'], 'resources': []}] +    required: false +    default: None +    aliases: [] +author: +- "Kenny Woodson <kwoodson@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: query a list of env vars on dc +  oc_clusterrole: +    name: myclusterrole +    state: list + +- name: Set the following variables. +  oc_clusterrole: +    name: myclusterrole +    rules: +      apiGroups: +      - "" +      attributeRestrictions: null +      verbs: [] +      resources: [] +''' diff --git a/roles/lib_openshift/src/lib/clusterrole.py b/roles/lib_openshift/src/lib/clusterrole.py new file mode 100644 index 000000000..8207fbc38 --- /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 DeploymentConfig''' +    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..628129832 --- /dev/null +++ b/roles/lib_openshift/src/lib/rule.py @@ -0,0 +1,143 @@ +# 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''' + +    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['apiGroups'], +                                rule['attributeRestrictions'], +                                rule['resources'], +                                rule['verbs'])) + +        return results diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index 135e2b752..9fa2a6c0e 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -89,6 +89,18 @@ oc_configmap.py:  - class/oc_configmap.py  - ansible/oc_configmap.py +oc_clusterrole.py: +- doc/generated +- doc/license +- lib/import.py +- doc/clusterrole +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- lib/rule.py +- lib/clusterrole.py +- class/oc_clusterrole.py +- ansible/oc_clusterrole.py +  oc_edit.py:  - doc/generated  - doc/license diff --git a/roles/lib_openshift/src/test/integration/oc_clusterrole.yml b/roles/lib_openshift/src/test/integration/oc_clusterrole.yml new file mode 100755 index 000000000..91b143f55 --- /dev/null +++ b/roles/lib_openshift/src/test/integration/oc_clusterrole.yml @@ -0,0 +1,106 @@ +#!/usr/bin/ansible-playbook --module-path=../../../library/ +## ./oc_configmap.yml -M ../../../library -e "cli_master_test=$OPENSHIFT_MASTER +--- +- hosts: "{{ cli_master_test }}" +  gather_facts: no +  user: root + +  post_tasks: +  - name: create a test project +    oc_project: +      name: test +      description: for tests only + +  ###### create test ########### +  - name: create a clusterrole +    oc_clusterrole: +      state: present +      name: operations +      rules: +      - apiGroups: +        - "" +        resources: +        - persistentvolumes +        attributeRestrictions: null +        verbs: +        - create +        - delete +        - deletecollection +        - get +        - list +        - patch +        - update +        - watch + +  - name: fetch the created clusterrole +    oc_clusterrole: +      name: operations +      state: list +    register: croleout + +  - debug: var=croleout + +  - name: assert clusterrole exists +    assert: +      that: +      - croleout.results.results.metadata.name == 'operations' +      - croleout.results.results.rules[0].resources[0] == 'persistentvolumes' +  ###### end create test ########### + +  ###### update test ########### +  - name: update a clusterrole +    oc_clusterrole: +      state: present +      name: operations +      rules: +      - apiGroups: +        - "" +        resources: +        - persistentvolumes +        - serviceaccounts +        - services +        attributeRestrictions: null +        verbs: +        - create +        - delete +        - deletecollection +        - get +        - list +        - patch +        - update +        - watch + +  - name: fetch the created clusterrole +    oc_clusterrole: +      name: operations +      state: list +    register: croleout + +  - debug: var=croleout + +  - name: assert clusterrole is updated +    assert: +      that: +      - croleout.results.results.metadata.name == 'operations' +      - "'persistentvolumes' in croleout.results.results.rules[0].resources" +      - "'serviceaccounts' in croleout.results.results.rules[0].resources" +      - "'services' in croleout.results.results.rules[0].resources" +  ###### end create test ########### + +  ###### delete test ########### +  - name: delete a clusterrole +    oc_clusterrole: +      state: absent +      name: operations + +  - name: fetch the clusterrole +    oc_clusterrole: +      name: operations +      state: list +    register: croleout + +  - debug: var=croleout + +  - name: assert operations does not exist +    assert: +      that: "'\"operations\" not found' in croleout.results.stderr" diff --git a/roles/lib_openshift/src/test/unit/test_oc_clusterrole.py b/roles/lib_openshift/src/test/unit/test_oc_clusterrole.py new file mode 100755 index 000000000..aa4518821 --- /dev/null +++ b/roles/lib_openshift/src/test/unit/test_oc_clusterrole.py @@ -0,0 +1,116 @@ +''' + Unit tests for oc clusterrole +''' + +import copy +import os +import sys +import unittest +import mock + +# Removing invalid variable names for tests so that I can +# keep them brief +# pylint: disable=invalid-name,no-name-in-module +# Disable import-error b/c our libraries aren't loaded in jenkins +# pylint: disable=import-error,wrong-import-position +# place class in our python path +module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library')  # noqa: E501 +sys.path.insert(0, module_path) +from oc_clusterrole import OCClusterRole  # noqa: E402 + + +class OCClusterRoleTest(unittest.TestCase): +    ''' +     Test class for OCClusterRole +    ''' + +    # run_ansible input parameters +    params = { +        'state': 'present', +        'name': 'operations', +        'rules': [ +            {'apiGroups': [''], +             'attributeRestrictions': None, +             'verbs': ['create', 'delete', 'deletecollection', +                       'get', 'list', 'patch', 'update', 'watch'], +             'resources': ['persistentvolumes']} +        ], +        'kubeconfig': '/etc/origin/master/admin.kubeconfig', +        'debug': False, +    } + +    @mock.patch('oc_clusterrole.locate_oc_binary') +    @mock.patch('oc_clusterrole.Utils.create_tmpfile_copy') +    @mock.patch('oc_clusterrole.Utils._write') +    @mock.patch('oc_clusterrole.OCClusterRole._run') +    def test_adding_a_clusterrole(self, mock_cmd, mock_write, mock_tmpfile_copy, mock_loc_binary): +        ''' Testing adding a project ''' + +        params = copy.deepcopy(OCClusterRoleTest.params) + +        clusterrole = '''{ +            "apiVersion": "v1", +            "kind": "ClusterRole", +            "metadata": { +                "creationTimestamp": "2017-03-27T14:19:09Z", +                "name": "operations", +                "resourceVersion": "23", +                "selfLink": "/oapi/v1/clusterrolesoperations", +                "uid": "57d358fe-12f8-11e7-874a-0ec502977670" +            }, +            "rules": [ +                { +                    "apiGroups": [ +                        "" +                    ], +                    "attributeRestrictions": null, +                    "resources": [ +                        "persistentvolumes" +                    ], +                    "verbs": [ +                        "create", +                        "delete", +                        "deletecollection", +                        "get", +                        "list", +                        "patch", +                        "update", +                        "watch" +                    ] +                } +            ] +        }''' + +        # Return values of our mocked function call. These get returned once per call. +        mock_cmd.side_effect = [ +            (1, '', 'Error from server: clusterrole "operations" not found'), +            (1, '', 'Error from server: namespaces "operations" not found'), +            (0, '', ''),  # created +            (0, clusterrole, ''),  # fetch it +        ] + +        mock_tmpfile_copy.side_effect = [ +            '/tmp/mocked_kubeconfig', +        ] + +        mock_loc_binary.side_effect = [ +            'oc', +        ] + +        # Act +        results = OCClusterRole.run_ansible(params, False) + +        # Assert +        self.assertTrue(results['changed']) +        self.assertEqual(results['results']['returncode'], 0) +        self.assertEqual(results['results']['results']['metadata']['name'], 'operations') +        self.assertEqual(results['state'], 'present') + +        # Making sure our mock was called as we expected +        mock_cmd.assert_has_calls([ +            mock.call(['oc', 'get', 'clusterrole', 'operations', '-o', 'json'], None), +            mock.call(['oc', 'get', 'clusterrole', 'operations', '-o', 'json'], None), +            mock.call(['oc', 'create', '-f', mock.ANY], None), +            mock.call(['oc', 'get', 'clusterrole', 'operations', '-o', 'json'], None), +        ]) + | 
