Bläddra i källkod

Merge pull request #3299 from kwoodson/oc_env

Adding oc_env to lib_openshift.
Scott Dodson 8 år sedan
förälder
incheckning
5780b4a0df

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1791 - 0
roles/lib_openshift/library/oc_env.py


+ 33 - 0
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()

+ 145 - 0
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}

+ 83 - 0
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
+'''

+ 11 - 0
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

+ 75 - 0
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."

+ 444 - 0
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()