diff options
author | Scott Dodson <sdodson@redhat.com> | 2017-02-13 11:55:22 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-13 11:55:22 -0500 |
commit | 5780b4a0df57af000ed4b357a85a394f2ce99a57 (patch) | |
tree | a4f6b37423446ca2ba8cf83017f0429a0474dd04 /roles/lib_openshift/src | |
parent | 97a7b64a370514759b22229d72aa3f1f85d46e17 (diff) | |
parent | 45c57201d4601d24e6131bf80800cb7f49d21af7 (diff) | |
download | openshift-5780b4a0df57af000ed4b357a85a394f2ce99a57.tar.gz openshift-5780b4a0df57af000ed4b357a85a394f2ce99a57.tar.bz2 openshift-5780b4a0df57af000ed4b357a85a394f2ce99a57.tar.xz openshift-5780b4a0df57af000ed4b357a85a394f2ce99a57.zip |
Merge pull request #3299 from kwoodson/oc_env
Adding oc_env to lib_openshift.
Diffstat (limited to 'roles/lib_openshift/src')
-rw-r--r-- | roles/lib_openshift/src/ansible/oc_env.py | 33 | ||||
-rw-r--r-- | roles/lib_openshift/src/class/oc_env.py | 145 | ||||
-rw-r--r-- | roles/lib_openshift/src/doc/env | 83 | ||||
-rw-r--r-- | roles/lib_openshift/src/sources.yml | 11 | ||||
-rwxr-xr-x | roles/lib_openshift/src/test/integration/oc_env.yml | 75 | ||||
-rwxr-xr-x | roles/lib_openshift/src/test/unit/oc_env.py | 444 |
6 files changed, 791 insertions, 0 deletions
diff --git a/roles/lib_openshift/src/ansible/oc_env.py b/roles/lib_openshift/src/ansible/oc_env.py new file mode 100644 index 000000000..e49295873 --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_env.py @@ -0,0 +1,33 @@ +# pylint: skip-file +# flake8: noqa + +def main(): + ''' + ansible oc module for environment variables + ''' + + 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'), + kind=dict(default='rc', choices=['dc', 'rc', 'pods'], type='str'), + namespace=dict(default='default', type='str'), + name=dict(default=None, required=True, type='str'), + env_vars=dict(default=None, type='dict'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + results = OCEnv.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_env.py b/roles/lib_openshift/src/class/oc_env.py new file mode 100644 index 000000000..d34c8234e --- /dev/null +++ b/roles/lib_openshift/src/class/oc_env.py @@ -0,0 +1,145 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCEnv(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + container_path = {"pod": "spec.containers[0].env", + "dc": "spec.template.spec.containers[0].env", + "rc": "spec.template.spec.containers[0].env", + } + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + namespace, + kind, + env_vars, + resource_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCEnv, self).__init__(namespace, kubeconfig) + self.kind = kind + self.name = resource_name + self.namespace = namespace + self.env_vars = env_vars + self.kubeconfig = kubeconfig + self.verbose = verbose + self._resource = None + + @property + def resource(self): + ''' property function for resource var''' + if not self._resource: + self.get() + return self._resource + + @resource.setter + def resource(self, data): + ''' setter function for resource var''' + self._resource = data + + def key_value_exists(self, key, value): + ''' return whether a key, value pair exists ''' + return self.resource.exists_env_value(key, value) + + def key_exists(self, key): + ''' return whether a key exists ''' + return self.resource.exists_env_key(key) + + def get(self): + '''return environment variables ''' + result = self._get(self.kind, self.name) + if result['returncode'] == 0: + if self.kind == 'dc': + self.resource = DeploymentConfig(content=result['results'][0]) + result['results'] = self.resource.get(OCEnv.container_path[self.kind]) or [] + return result + + def delete(self): + ''' delete environment variables ''' + if self.resource.delete_env_var(self.env_vars.keys()): + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + return {'returncode': 0, 'changed': False} + + def put(self): + '''place env vars into dc ''' + for update_key, update_value in self.env_vars.items(): + self.resource.update_env_var(update_key, update_value) + + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + # pylint: disable=too-many-return-statements + @staticmethod + def run_ansible(params, check_mode): + '''run the idempotent ansible code''' + + ocenv = OCEnv(params['namespace'], + params['kind'], + params['env_vars'], + resource_name=params['name'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = ocenv.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': "list"} + + ######## + # Delete + ######## + if state == 'absent': + for key in params.get('env_vars', {}).keys(): + if ocenv.resource.exists_env_key(key): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = ocenv.delete() + + return {'changed': True, 'state': 'absent'} + + return {'changed': False, 'state': 'absent'} + + if state == 'present': + ######## + # Create + ######## + for key, value in params.get('env_vars', {}).items(): + if not ocenv.key_value_exists(key, value): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = ocenv.put() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = ocenv.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval['results'], 'state': 'present'} + + return {'changed': False, 'results': api_rval['results'], 'state': 'present'} + + + return {'failed': True, + 'changed': False, + 'msg': 'Unknown state passed. %s' % state} diff --git a/roles/lib_openshift/src/doc/env b/roles/lib_openshift/src/doc/env new file mode 100644 index 000000000..36edcd211 --- /dev/null +++ b/roles/lib_openshift/src/doc/env @@ -0,0 +1,83 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_env +short_description: Modify, and idempotently manage openshift environment variables on pods, deploymentconfigs, and replication controllers. +description: + - Modify openshift environment variables programmatically. +options: + state: + description: + - Supported states, present, absent, list + - present - will ensure object is created or updated to the value specified + - list - will return a list of environment variables + - absent - will remove the environment varibale from the object + 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: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] + kind: + description: + - The kind attribute of the object. + required: False + default: dc + choices: + - rc + - dc + - pods + aliases: [] + env_vars: + description: + - The environment variables to insert. The format is a dict of value pairs. + - e.g. {key1: value1, key2: value2}) + 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_env: + kind: dc + name: myawesomedc + namespace: phpapp + +- name: Set the following variables. + oc_env: + kind: dc + name: myawesomedc + namespace: phpapp + env_vars: + SUPER_TURBO_MODE: 'true' + ENABLE_PORTS: 'false' + SERVICE_PORT: 9999 +''' diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index e9056655d..091aaef2e 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -19,6 +19,17 @@ oc_edit.py: - class/oc_edit.py - ansible/oc_edit.py +oc_env.py: +- doc/generated +- doc/license +- lib/import.py +- doc/env +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- lib/deploymentconfig.py +- class/oc_env.py +- ansible/oc_env.py + oc_label.py: - doc/generated - doc/license diff --git a/roles/lib_openshift/src/test/integration/oc_env.yml b/roles/lib_openshift/src/test/integration/oc_env.yml new file mode 100755 index 000000000..cbb97ed46 --- /dev/null +++ b/roles/lib_openshift/src/test/integration/oc_env.yml @@ -0,0 +1,75 @@ +#!/usr/bin/ansible-playbook --module-path=../../../library/ +# ./oc_env.yml -e "cli_master_test=$OPENSHIFT_MASTER +--- +- hosts: "{{ cli_master_test }}" + gather_facts: no + user: root + vars: + my_env_var: + SOMEKEY: SOMEVALUE + + check_env_var: + name: DEFAULT_CERTIFICATE_DIR + value: /etc/pki/tls/private + + tasks: + - name: list environment variables from router dc + oc_env: + state: list + name: router + namespace: default + kind: dc + register: envout + - debug: var=envout + + - assert: + that: + - "'{{ check_env_var.name }}' == '{{ envout.results[0].name }}'" + - "{{ envout.results|length }} > 0" + msg: "Did not find environment variables." + + - name: list environment variables from router dc + oc_env: + state: present + name: router + namespace: default + kind: dc + env_vars: "{{ my_env_var }}" + register: envout + - debug: var=envout + + - assert: + that: + - "'SOMEKEY' == '{{ envout.results[-1].name }}'" + - "'SOMEVALUE' == '{{ envout.results[-1].value }}'" + msg: "Did not find updated environment variables." + + - name: remove environment variables from router dc + oc_env: + state: absent + name: router + namespace: default + kind: dc + env_vars: "{{ my_env_var }}" + register: envout + - debug: var=envout + + - assert: + that: + - envout.changed == True + msg: "state: Absent failed." + + - name: list environment variables from router dc + oc_env: + state: list + name: router + namespace: default + kind: dc + register: envout + - debug: var=envout + + - assert: + that: + - "'SOMEKEY' != '{{ envout.results[-1].name }}'" + - "'SOMEVALUE' != '{{ envout.results[-1].value }}'" + msg: "Did find updated environment variables." diff --git a/roles/lib_openshift/src/test/unit/oc_env.py b/roles/lib_openshift/src/test/unit/oc_env.py new file mode 100755 index 000000000..15bd7e464 --- /dev/null +++ b/roles/lib_openshift/src/test/unit/oc_env.py @@ -0,0 +1,444 @@ +#!/usr/bin/env python2 +''' + Unit tests for oc_env +''' +# To run: +# ./oc_env.py +# +# . +# Ran 1 test in 0.002s +# +# OK + +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_env import OCEnv # noqa: E402 + + +class OCEnvTest(unittest.TestCase): + ''' + Test class for OCEnv + ''' + + def setUp(self): + ''' setup method will create a file and set to known configuration ''' + pass + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_listing_all_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Testing listing all environment variables from a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'list', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'env_vars': None, + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + } + + dc_results = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results, ''), # First call to the mock + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertFalse(results['changed']) + for env_var in results['results']: + if env_var == {'name': 'DEFAULT_CERTIFICATE_DIR', 'value': '/etc/pki/tls/private'}: + break + else: + self.fail('Did not find environment variables in results.') + self.assertEqual(results['state'], 'list') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_adding_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Test add environment variables to a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'present', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + 'env_vars': {'SOMEKEY': 'SOMEVALUE'}, + } + + dc_results = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + dc_results_after = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + }, + { + "name": "SOMEKEY", + "value": "SOMEVALUE" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results, ''), + (0, dc_results, ''), + (0, dc_results_after, ''), + (0, dc_results_after, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertTrue(results['changed']) + for env_var in results['results']: + if env_var == {'name': 'SOMEKEY', 'value': 'SOMEVALUE'}: + break + else: + self.fail('Did not find environment variables in results.') + self.assertEqual(results['state'], 'present') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_removing_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Test add environment variables to a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'absent', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + 'env_vars': {'SOMEKEY': 'SOMEVALUE'}, + } + + dc_results_before = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + }, + { + "name": "SOMEKEY", + "value": "SOMEVALUE" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results_before, ''), + (0, dc_results_before, ''), + (0, '', ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertTrue(results['changed']) + self.assertEqual(results['state'], 'absent') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + def tearDown(self): + '''TearDown method''' + pass + + +if __name__ == "__main__": + unittest.main() |