From 623ff39443536eebd6410287dac32660e462804f Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Tue, 9 Jan 2018 07:22:46 -0500 Subject: [PATCH] Separate common and raw (#34563) --- lib/ansible/module_utils/k8s/common.py | 199 ++--------------- lib/ansible/module_utils/k8s/lookup.py | 22 +- lib/ansible/module_utils/k8s/raw.py | 211 ++++++++++++++++++ lib/ansible/module_utils/k8s/scale.py | 23 +- lib/ansible/modules/clustering/k8s/k8s_raw.py | 4 +- .../clustering/openshift/openshift_raw.py | 4 +- test/sanity/pylint/ignore.txt | 2 - 7 files changed, 247 insertions(+), 218 deletions(-) create mode 100644 lib/ansible/module_utils/k8s/raw.py diff --git a/lib/ansible/module_utils/k8s/common.py b/lib/ansible/module_utils/k8s/common.py index 369b221de3..315c4ff79f 100644 --- a/lib/ansible/module_utils/k8s/common.py +++ b/lib/ansible/module_utils/k8s/common.py @@ -30,10 +30,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.k8s.helper import\ AnsibleMixin,\ - HAS_STRING_UTILS,\ - COMMON_ARG_SPEC,\ - AUTH_ARG_SPEC,\ - OPENSHIFT_ARG_SPEC + HAS_STRING_UTILS try: from openshift.helper.kubernetes import KubernetesObjectHelper @@ -97,8 +94,12 @@ class KubernetesAnsibleModuleHelper(AnsibleMixin, KubernetesObjectHelper): class KubernetesAnsibleModule(AnsibleModule): + resource_definition = None + api_version = None + kind = None + helper = None - def __init__(self): + def __init__(self, *args, **kwargs): if not HAS_K8S_MODULE_HELPER: raise Exception( @@ -115,48 +116,14 @@ class KubernetesAnsibleModule(AnsibleModule): "This module requires Python string utils. Try `pip install python-string-utils`" ) - mutually_exclusive = [ - ('resource_definition', 'src'), - ] - - AnsibleModule.__init__(self, - argument_spec=self._argspec, - supports_check_mode=True, - mutually_exclusive=mutually_exclusive) - - self.kind = self.params.pop('kind') - self.api_version = self.params.pop('api_version') - self.resource_definition = self.params.pop('resource_definition') - self.src = self.params.pop('src') - if self.src: - self.resource_definition = self.load_resource_definition(self.src) - - if self.resource_definition: - self.api_version = self.resource_definition.get('apiVersion') - self.kind = self.resource_definition.get('kind') - - self.api_version = self.api_version.lower() - self.kind = to_snake(self.kind) - - if not self.api_version: - self.fail_json( - msg=("Error: no api_version specified. Use the api_version parameter, or provide it as part of a ", - "resource_definition.") - ) - if not self.kind: - self.fail_json( - msg="Error: no kind specified. Use the kind parameter, or provide it as part of a resource_definition" - ) - - self.helper = self._get_helper(self.api_version, self.kind) + kwargs['argument_spec'] = self.argspec + AnsibleModule.__init__(self, *args, **kwargs) @property - def _argspec(self): - argspec = copy.deepcopy(COMMON_ARG_SPEC) - argspec.update(copy.deepcopy(AUTH_ARG_SPEC)) - return argspec + def argspec(self): + raise NotImplementedError() - def _get_helper(self, api_version, kind): + def get_helper(self, api_version, kind): try: helper = KubernetesAnsibleModuleHelper(api_version=api_version, kind=kind, debug=False) helper.get_model(api_version, kind) @@ -165,86 +132,7 @@ class KubernetesAnsibleModule(AnsibleModule): self.fail_json(msg="Error initializing module helper: {0}".format(exc.message)) def execute_module(self): - if self.resource_definition: - resource_params = self.resource_to_parameters(self.resource_definition) - self.params.update(resource_params) - - self._authenticate() - - state = self.params.pop('state', None) - force = self.params.pop('force', False) - name = self.params.get('name') - namespace = self.params.get('namespace') - existing = None - - self._remove_aliases() - - return_attributes = dict(changed=False, result=dict()) - - if self.helper.base_model_name_snake.endswith('list'): - k8s_obj = self._read(name, namespace) - return_attributes['result'] = k8s_obj.to_dict() - self.exit_json(**return_attributes) - - try: - existing = self.helper.get_object(name, namespace) - except KubernetesException as exc: - self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc.message), - error=exc.value.get('status')) - - if state == 'absent': - if not existing: - # The object already does not exist - self.exit_json(**return_attributes) - else: - # Delete the object - if not self.check_mode: - try: - self.helper.delete_object(name, namespace) - except KubernetesException as exc: - self.fail_json(msg="Failed to delete object: {0}".format(exc.message), - error=exc.value.get('status')) - return_attributes['changed'] = True - self.exit_json(**return_attributes) - else: - if not existing: - k8s_obj = self._create(namespace) - return_attributes['result'] = k8s_obj.to_dict() - return_attributes['changed'] = True - self.exit_json(**return_attributes) - - if existing and force: - k8s_obj = None - request_body = self.helper.request_body_from_params(self.params) - if not self.check_mode: - try: - k8s_obj = self.helper.replace_object(name, namespace, body=request_body) - except KubernetesException as exc: - self.fail_json(msg="Failed to replace object: {0}".format(exc.message), - error=exc.value.get('status')) - return_attributes['result'] = k8s_obj.to_dict() - return_attributes['changed'] = True - self.exit_json(**return_attributes) - - # Check if existing object should be patched - k8s_obj = copy.deepcopy(existing) - try: - self.helper.object_from_params(self.params, obj=k8s_obj) - except KubernetesException as exc: - self.fail_json(msg="Failed to patch object: {0}".format(exc.message)) - match, diff = self.helper.objects_match(self.helper.fix_serialization(existing), k8s_obj) - if match: - return_attributes['result'] = existing.to_dict() - self.exit_json(**return_attributes) - # Differences exist between the existing obj and requested params - if not self.check_mode: - try: - k8s_obj = self.helper.patch_object(name, namespace, k8s_obj) - except KubernetesException as exc: - self.fail_json(msg="Failed to patch object: {0}".format(exc.message)) - return_attributes['result'] = k8s_obj.to_dict() - return_attributes['changed'] = True - self.exit_json(**return_attributes) + raise NotImplementedError() def exit_json(self, **return_attributes): """ Filter any sensitive data that we don't want logged """ @@ -257,7 +145,7 @@ class KubernetesAnsibleModule(AnsibleModule): remove_secret_data(item) super(KubernetesAnsibleModule, self).exit_json(**return_attributes) - def _authenticate(self): + def authenticate(self): try: auth_options = {} auth_args = ('host', 'api_key', 'kubeconfig', 'context', 'username', 'password', @@ -269,40 +157,16 @@ class KubernetesAnsibleModule(AnsibleModule): except KubernetesException as e: self.fail_json(msg='Error loading config', error=str(e)) - def _remove_aliases(self): + def remove_aliases(self): """ The helper doesn't know what to do with aliased keys """ - for k, v in iteritems(self._argspec): + for k, v in iteritems(self.argspec): if 'aliases' in v: for alias in v['aliases']: if alias in self.params: self.params.pop(alias) - def _create(self, namespace): - request_body = None - k8s_obj = None - try: - request_body = self.helper.request_body_from_params(self.params) - except KubernetesException as exc: - self.fail_json(msg="Failed to create object: {0}".format(exc.message)) - if not self.check_mode: - try: - k8s_obj = self.helper.create_object(namespace, body=request_body) - except KubernetesException as exc: - self.fail_json(msg="Failed to create object: {0}".format(exc.message), - error=exc.value.get('status')) - return k8s_obj - - def _read(self, name, namespace): - k8s_obj = None - try: - k8s_obj = self.helper.get_object(name, namespace) - except KubernetesException as exc: - self.fail_json(msg='Failed to retrieve requested object', - error=exc.value.get('status')) - return k8s_obj - def load_resource_definition(self, src): """ Load the requested src path """ result = None @@ -354,41 +218,12 @@ class OpenShiftAnsibleModuleHelper(AnsibleMixin, OpenShiftObjectHelper): pass -class OpenShiftAnsibleModule(KubernetesAnsibleModule): - def __init__(self): - super(OpenShiftAnsibleModule, self).__init__() +class OpenShiftAnsibleModuleMixin(object): - @property - def _argspec(self): - args = super(OpenShiftAnsibleModule, self)._argspec - args.update(copy.deepcopy(OPENSHIFT_ARG_SPEC)) - return args - - def _get_helper(self, api_version, kind): + def get_helper(self, api_version, kind): try: helper = OpenShiftAnsibleModuleHelper(api_version=api_version, kind=kind, debug=False) helper.get_model(api_version, kind) return helper except KubernetesException as exc: self.fail_json(msg="Error initializing module helper: {0}".format(exc.message)) - - def _create(self, namespace): - if self.kind.lower() == 'project': - return self._create_project() - return super(OpenShiftAnsibleModule, self)._create(namespace) - - def _create_project(self): - new_obj = None - k8s_obj = None - try: - new_obj = self.helper.object_from_params(self.params) - except KubernetesException as exc: - self.fail_json(msg="Failed to create object: {}".format(exc.message)) - try: - k8s_obj = self.helper.create_project(metadata=new_obj.metadata, - display_name=self.params.get('display_name'), - description=self.params.get('description')) - except KubernetesException as exc: - self.fail_json(msg='Failed to retrieve requested object', - error=exc.value.get('status')) - return k8s_obj diff --git a/lib/ansible/module_utils/k8s/lookup.py b/lib/ansible/module_utils/k8s/lookup.py index 6058f59309..f5311669c2 100644 --- a/lib/ansible/module_utils/k8s/lookup.py +++ b/lib/ansible/module_utils/k8s/lookup.py @@ -21,12 +21,11 @@ from __future__ import absolute_import, division, print_function import json import os -from ansible.module_utils.k8s.common import DateTimeEncoder, remove_secret_data, to_snake +from ansible.module_utils.k8s.common import OpenShiftAnsibleModuleMixin, DateTimeEncoder, remove_secret_data, to_snake from ansible.module_utils.k8s.helper import AUTH_ARG_SPEC try: from openshift.helper.kubernetes import KubernetesObjectHelper - from openshift.helper.openshift import OpenShiftObjectHelper from openshift.helper.exceptions import KubernetesException HAS_K8S_MODULE_HELPER = True except ImportError as exc: @@ -88,7 +87,7 @@ class KubernetesLookup(object): ) self.kind = to_snake(self.kind) - self.helper = self.get_helper() + self.helper = self.get_helper(self.api_version, self.kind) for arg in AUTH_ARG_SPEC: self.connection[arg] = kwargs.get(arg) @@ -110,10 +109,10 @@ class KubernetesLookup(object): with open('loggit.txt', 'a') as f: f.write(msg + '\n') - def get_helper(self): + def get_helper(self, api_version, kind): try: - helper = KubernetesObjectHelper(api_version=self.api_version, kind=self.kind, debug=False) - helper.get_model(self.api_version, self.kind) + helper = KubernetesObjectHelper(api_version=api_version, kind=kind, debug=False) + helper.get_model(api_version, kind) return helper except KubernetesException as exc: raise Exception("Error initializing helper: {0}".format(exc.message)) @@ -208,12 +207,5 @@ class KubernetesLookup(object): return response -class OpenShiftLookup(KubernetesLookup): - - def get_helper(self): - try: - helper = OpenShiftObjectHelper(api_version=self.api_version, kind=self.kind, debug=False) - helper.get_model(self.api_version, self.kind) - return helper - except KubernetesException as exc: - raise Exception("Error initializing helper: {0}".format(exc.message)) +class OpenShiftLookup(OpenShiftAnsibleModuleMixin, KubernetesLookup): + pass diff --git a/lib/ansible/module_utils/k8s/raw.py b/lib/ansible/module_utils/k8s/raw.py new file mode 100644 index 0000000000..7b7a68ca37 --- /dev/null +++ b/lib/ansible/module_utils/k8s/raw.py @@ -0,0 +1,211 @@ +# +# Copyright 2018 Red Hat | Ansible +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +from __future__ import absolute_import, division, print_function + +import copy + +from ansible.module_utils.k8s.helper import COMMON_ARG_SPEC, AUTH_ARG_SPEC, OPENSHIFT_ARG_SPEC +from ansible.module_utils.k8s.common import KubernetesAnsibleModule, OpenShiftAnsibleModuleMixin, to_snake + +try: + from openshift.helper.exceptions import KubernetesException +except ImportError: + # Exception handled in common + pass + + +class KubernetesRawModule(KubernetesAnsibleModule): + + def __init__(self, *args, **kwargs): + mutually_exclusive = [ + ('resource_definition', 'src'), + ] + + KubernetesAnsibleModule.__init__(self, *args, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + **kwargs) + + self.kind = self.params.pop('kind') + self.api_version = self.params.pop('api_version') + self.resource_definition = self.params.pop('resource_definition') + self.src = self.params.pop('src') + if self.src: + self.resource_definition = self.load_resource_definition(self.src) + + if self.resource_definition: + self.api_version = self.resource_definition.get('apiVersion') + self.kind = self.resource_definition.get('kind') + + self.api_version = self.api_version.lower() + self.kind = to_snake(self.kind) + + if not self.api_version: + self.fail_json( + msg=("Error: no api_version specified. Use the api_version parameter, or provide it as part of a ", + "resource_definition.") + ) + if not self.kind: + self.fail_json( + msg="Error: no kind specified. Use the kind parameter, or provide it as part of a resource_definition" + ) + + self.helper = self.get_helper(self.api_version, self.kind) + + @property + def argspec(self): + argspec = copy.deepcopy(COMMON_ARG_SPEC) + argspec.update(copy.deepcopy(AUTH_ARG_SPEC)) + return argspec + + def execute_module(self): + if self.resource_definition: + resource_params = self.resource_to_parameters(self.resource_definition) + self.params.update(resource_params) + + self.authenticate() + + state = self.params.pop('state', None) + force = self.params.pop('force', False) + name = self.params.get('name') + namespace = self.params.get('namespace') + existing = None + + self.remove_aliases() + + return_attributes = dict(changed=False, result=dict()) + + if self.helper.base_model_name_snake.endswith('list'): + k8s_obj = self._read(name, namespace) + return_attributes['result'] = k8s_obj.to_dict() + self.exit_json(**return_attributes) + + try: + existing = self.helper.get_object(name, namespace) + except KubernetesException as exc: + self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc.message), + error=exc.value.get('status')) + + if state == 'absent': + if not existing: + # The object already does not exist + self.exit_json(**return_attributes) + else: + # Delete the object + if not self.check_mode: + try: + self.helper.delete_object(name, namespace) + except KubernetesException as exc: + self.fail_json(msg="Failed to delete object: {0}".format(exc.message), + error=exc.value.get('status')) + return_attributes['changed'] = True + self.exit_json(**return_attributes) + else: + if not existing: + k8s_obj = self._create(namespace) + return_attributes['result'] = k8s_obj.to_dict() + return_attributes['changed'] = True + self.exit_json(**return_attributes) + + if existing and force: + k8s_obj = None + request_body = self.helper.request_body_from_params(self.params) + if not self.check_mode: + try: + k8s_obj = self.helper.replace_object(name, namespace, body=request_body) + except KubernetesException as exc: + self.fail_json(msg="Failed to replace object: {0}".format(exc.message), + error=exc.value.get('status')) + return_attributes['result'] = k8s_obj.to_dict() + return_attributes['changed'] = True + self.exit_json(**return_attributes) + + # Check if existing object should be patched + k8s_obj = copy.deepcopy(existing) + try: + self.helper.object_from_params(self.params, obj=k8s_obj) + except KubernetesException as exc: + self.fail_json(msg="Failed to patch object: {0}".format(exc.message)) + match, diff = self.helper.objects_match(self.helper.fix_serialization(existing), k8s_obj) + if match: + return_attributes['result'] = existing.to_dict() + self.exit_json(**return_attributes) + # Differences exist between the existing obj and requested params + if not self.check_mode: + try: + k8s_obj = self.helper.patch_object(name, namespace, k8s_obj) + except KubernetesException as exc: + self.fail_json(msg="Failed to patch object: {0}".format(exc.message)) + return_attributes['result'] = k8s_obj.to_dict() + return_attributes['changed'] = True + self.exit_json(**return_attributes) + + def _create(self, namespace): + request_body = None + k8s_obj = None + try: + request_body = self.helper.request_body_from_params(self.params) + except KubernetesException as exc: + self.fail_json(msg="Failed to create object: {0}".format(exc.message)) + if not self.check_mode: + try: + k8s_obj = self.helper.create_object(namespace, body=request_body) + except KubernetesException as exc: + self.fail_json(msg="Failed to create object: {0}".format(exc.message), + error=exc.value.get('status')) + return k8s_obj + + def _read(self, name, namespace): + k8s_obj = None + try: + k8s_obj = self.helper.get_object(name, namespace) + except KubernetesException as exc: + self.fail_json(msg='Failed to retrieve requested object', + error=exc.value.get('status')) + return k8s_obj + + +class OpenShiftRawModule(OpenShiftAnsibleModuleMixin, KubernetesRawModule): + + @property + def argspec(self): + args = super(OpenShiftRawModule, self).argspec + args.update(copy.deepcopy(OPENSHIFT_ARG_SPEC)) + return args + + def _create(self, namespace): + if self.kind.lower() == 'project': + return self._create_project() + return KubernetesRawModule._create(self, namespace) + + def _create_project(self): + new_obj = None + k8s_obj = None + try: + new_obj = self.helper.object_from_params(self.params) + except KubernetesException as exc: + self.fail_json(msg="Failed to create object: {0}".format(exc.message)) + try: + k8s_obj = self.helper.create_project(metadata=new_obj.metadata, + display_name=self.params.get('display_name'), + description=self.params.get('description')) + except KubernetesException as exc: + self.fail_json(msg='Failed to retrieve requested object', + error=exc.value.get('status')) + return k8s_obj diff --git a/lib/ansible/module_utils/k8s/scale.py b/lib/ansible/module_utils/k8s/scale.py index 981e767d24..3441bb89fc 100644 --- a/lib/ansible/module_utils/k8s/scale.py +++ b/lib/ansible/module_utils/k8s/scale.py @@ -23,11 +23,12 @@ import math import time from ansible.module_utils.six import iteritems -from ansible.module_utils.k8s.common import KubernetesAnsibleModule, OpenShiftAnsibleModuleHelper +from ansible.module_utils.k8s.common import OpenShiftAnsibleModuleMixin +from ansible.module_utils.k8s.raw import KubernetesRawModule from ansible.module_utils.k8s.helper import AUTH_ARG_SPEC, COMMON_ARG_SPEC try: - from kubernetes import watch + from openshift import watch from openshift.helper.exceptions import KubernetesException except ImportError as exc: class KubernetesException(Exception): @@ -43,14 +44,14 @@ SCALE_ARG_SPEC = { } -class KubernetesAnsibleScaleModule(KubernetesAnsibleModule): +class KubernetesAnsibleScaleModule(KubernetesRawModule): def execute_module(self): if self.resource_definition: resource_params = self.resource_to_parameters(self.resource_definition) self.params.update(resource_params) - self._authenticate() + self.authenticate() name = self.params.get('name') namespace = self.params.get('namespace') @@ -110,7 +111,7 @@ class KubernetesAnsibleScaleModule(KubernetesAnsibleModule): return parameters @property - def _argspec(self): + def argspec(self): args = copy.deepcopy(COMMON_ARG_SPEC) args.pop('state') args.pop('force') @@ -233,13 +234,5 @@ class KubernetesAnsibleScaleModule(KubernetesAnsibleModule): return obj -class OpenShiftAnsibleScaleModule(KubernetesAnsibleScaleModule): - - def _get_helper(self, api_version, kind): - helper = None - try: - helper = OpenShiftAnsibleModuleHelper(api_version=api_version, kind=kind, debug=False) - helper.get_model(api_version, kind) - except KubernetesException as exc: - self.exit_json(msg="Error initializing module helper {}".format(exc.message)) - return helper +class OpenShiftAnsibleScaleModule(OpenShiftAnsibleModuleMixin, KubernetesAnsibleScaleModule): + pass diff --git a/lib/ansible/modules/clustering/k8s/k8s_raw.py b/lib/ansible/modules/clustering/k8s/k8s_raw.py index fe97c1d738..87b143f000 100644 --- a/lib/ansible/modules/clustering/k8s/k8s_raw.py +++ b/lib/ansible/modules/clustering/k8s/k8s_raw.py @@ -152,11 +152,11 @@ result: type: list ''' -from ansible.module_utils.k8s.common import KubernetesAnsibleModule +from ansible.module_utils.k8s.raw import KubernetesRawModule def main(): - KubernetesAnsibleModule().execute_module() + KubernetesRawModule().execute_module() if __name__ == '__main__': diff --git a/lib/ansible/modules/clustering/openshift/openshift_raw.py b/lib/ansible/modules/clustering/openshift/openshift_raw.py index b1e086c2cc..3059f8592a 100644 --- a/lib/ansible/modules/clustering/openshift/openshift_raw.py +++ b/lib/ansible/modules/clustering/openshift/openshift_raw.py @@ -193,11 +193,11 @@ result: type: list ''' -from ansible.module_utils.k8s.common import OpenShiftAnsibleModule +from ansible.module_utils.k8s.raw import OpenShiftRawModule def main(): - OpenShiftAnsibleModule().execute_module() + OpenShiftRawModule().execute_module() if __name__ == '__main__': diff --git a/test/sanity/pylint/ignore.txt b/test/sanity/pylint/ignore.txt index f8caf43efc..ff6c2b6324 100644 --- a/test/sanity/pylint/ignore.txt +++ b/test/sanity/pylint/ignore.txt @@ -6,8 +6,6 @@ hacking/cherrypick.py ansible-format-automatic-specification hacking/metadata-tool.py ansible-format-automatic-specification lib/ansible/cli/adhoc.py syntax-error 3.7 lib/ansible/module_utils/dimensiondata.py ansible-format-automatic-specification -lib/ansible/module_utils/k8s/common.py ansible-format-automatic-specification -lib/ansible/module_utils/k8s/scale.py ansible-format-automatic-specification lib/ansible/module_utils/network/aci/aci.py ansible-format-automatic-specification lib/ansible/module_utils/network/iosxr/iosxr.py ansible-format-automatic-specification lib/ansible/module_utils/ovirt.py ansible-format-automatic-specification