From 353a24669e7116b40a9c85367277d19040eb75ea Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Thu, 2 Feb 2017 12:39:13 -0500 Subject: Adding test cases. --- roles/lib_openshift/library/oc_process.py | 31 +- roles/lib_openshift/src/ansible/oc_process.py | 2 +- roles/lib_openshift/src/class/oc_process.py | 21 +- .../src/test/integration/oc_process.yml | 83 ++++ roles/lib_openshift/src/test/unit/oc_process.py | 473 +++++++++++++++++++++ 5 files changed, 583 insertions(+), 27 deletions(-) create mode 100755 roles/lib_openshift/src/test/integration/oc_process.yml create mode 100755 roles/lib_openshift/src/test/unit/oc_process.py diff --git a/roles/lib_openshift/library/oc_process.py b/roles/lib_openshift/library/oc_process.py index 820e5753d..ee75d8f84 100644 --- a/roles/lib_openshift/library/oc_process.py +++ b/roles/lib_openshift/library/oc_process.py @@ -902,11 +902,13 @@ class OpenShiftCLI(object): def _run(self, cmds, input_data): ''' Actually executes the command. This makes mocking easier. ''' + curr_env = os.environ.copy() + curr_env.update({'KUBECONFIG': self.kubeconfig}) proc = subprocess.Popen(cmds, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env={'KUBECONFIG': self.kubeconfig}) + env=curr_env) stdout, stderr = proc.communicate(input_data) @@ -917,9 +919,9 @@ class OpenShiftCLI(object): '''Base command for oc ''' cmds = [] if oadm: - cmds = ['/usr/bin/oadm'] + cmds = ['oadm'] else: - cmds = ['/usr/bin/oc'] + cmds = ['oc'] if self.all_namespaces: cmds.extend(['--all-namespaces']) @@ -1228,7 +1230,7 @@ class OpenShiftCLIConfig(object): # pylint: disable=too-many-instance-attributes -class Process(OpenShiftCLI): +class OCProcess(OpenShiftCLI): ''' Class to wrap the oc command line tools ''' # pylint allows 5. we need 6 @@ -1242,7 +1244,7 @@ class Process(OpenShiftCLI): tdata=None, verbose=False): ''' Constructor for OpenshiftOC ''' - super(Process, self).__init__(namespace, kubeconfig) + super(OCProcess, self).__init__(namespace, kubeconfig) self.namespace = namespace self.name = tname self.data = tdata @@ -1348,7 +1350,7 @@ class Process(OpenShiftCLI): def run_ansible(params, check_mode): '''run the ansible idempotent code''' - ocprocess = Process(params['namespace'], + ocprocess = OCProcess(params['namespace'], params['template_name'], params['params'], params['create'], @@ -1364,7 +1366,7 @@ class Process(OpenShiftCLI): if api_rval['returncode'] != 0: return {"failed": True, "msg" : api_rval} - return {"changed" : False, "results": api_rval, "state": "list") + return {"changed" : False, "results": api_rval, "state": "list"} elif state == 'present': if not ocprocess.exists() or not params['reconcile']: @@ -1382,7 +1384,10 @@ class Process(OpenShiftCLI): if api_rval['returncode'] != 0: return {"failed": True, "msg": api_rval} - return {"changed": True, "results": api_rval, "state": "present") + if params['create']: + return {"changed": True, "results": api_rval, "state": "present"} + + return {"changed": False, "results": api_rval, "state": "present"} # verify results update = False @@ -1397,18 +1402,14 @@ class Process(OpenShiftCLI): update = True if not update: - return {"changed": update, "results": api_rval, "state": "present") + return {"changed": update, "results": api_rval, "state": "present"} for cmd in rval: if cmd['returncode'] != 0: - return {"failed": True, "changed": update, "results": rval, "state": "present") + return {"failed": True, "changed": update, "results": rval, "state": "present"} return {"changed": update, "results": rval, "state": "present"} - return {"failed": True, - "changed": False, - "results": 'Unknown state passed ({0}). '.format(state), - "state": "unknown") # -*- -*- -*- End included fragment: class/oc_process.py -*- -*- -*- @@ -1436,7 +1437,7 @@ def main(): ) - rval = Process.run_ansible(module.params, module.check_mode) + rval = OCProcess.run_ansible(module.params, module.check_mode) if 'failed' in rval: module.fail_json(**rval) diff --git a/roles/lib_openshift/src/ansible/oc_process.py b/roles/lib_openshift/src/ansible/oc_process.py index b01c6aa86..5faa46aef 100644 --- a/roles/lib_openshift/src/ansible/oc_process.py +++ b/roles/lib_openshift/src/ansible/oc_process.py @@ -23,7 +23,7 @@ def main(): ) - rval = Process.run_ansible(module.params, module.check_mode) + rval = OCProcess.run_ansible(module.params, module.check_mode) if 'failed' in rval: module.fail_json(**rval) diff --git a/roles/lib_openshift/src/class/oc_process.py b/roles/lib_openshift/src/class/oc_process.py index d8e5015ed..6cb5ec8c9 100644 --- a/roles/lib_openshift/src/class/oc_process.py +++ b/roles/lib_openshift/src/class/oc_process.py @@ -3,7 +3,7 @@ # pylint: disable=too-many-instance-attributes -class Process(OpenShiftCLI): +class OCProcess(OpenShiftCLI): ''' Class to wrap the oc command line tools ''' # pylint allows 5. we need 6 @@ -17,7 +17,7 @@ class Process(OpenShiftCLI): tdata=None, verbose=False): ''' Constructor for OpenshiftOC ''' - super(Process, self).__init__(namespace, kubeconfig) + super(OCProcess, self).__init__(namespace, kubeconfig) self.namespace = namespace self.name = tname self.data = tdata @@ -123,7 +123,7 @@ class Process(OpenShiftCLI): def run_ansible(params, check_mode): '''run the ansible idempotent code''' - ocprocess = Process(params['namespace'], + ocprocess = OCProcess(params['namespace'], params['template_name'], params['params'], params['create'], @@ -139,7 +139,7 @@ class Process(OpenShiftCLI): if api_rval['returncode'] != 0: return {"failed": True, "msg" : api_rval} - return {"changed" : False, "results": api_rval, "state": "list") + return {"changed" : False, "results": api_rval, "state": "list"} elif state == 'present': if not ocprocess.exists() or not params['reconcile']: @@ -157,7 +157,10 @@ class Process(OpenShiftCLI): if api_rval['returncode'] != 0: return {"failed": True, "msg": api_rval} - return {"changed": True, "results": api_rval, "state": "present") + if params['create']: + return {"changed": True, "results": api_rval, "state": "present"} + + return {"changed": False, "results": api_rval, "state": "present"} # verify results update = False @@ -172,15 +175,11 @@ class Process(OpenShiftCLI): update = True if not update: - return {"changed": update, "results": api_rval, "state": "present") + return {"changed": update, "results": api_rval, "state": "present"} for cmd in rval: if cmd['returncode'] != 0: - return {"failed": True, "changed": update, "results": rval, "state": "present") + return {"failed": True, "changed": update, "results": rval, "state": "present"} return {"changed": update, "results": rval, "state": "present"} - return {"failed": True, - "changed": False, - "results": 'Unknown state passed ({0}). '.format(state), - "state": "unknown") diff --git a/roles/lib_openshift/src/test/integration/oc_process.yml b/roles/lib_openshift/src/test/integration/oc_process.yml new file mode 100755 index 000000000..7ea4c6b99 --- /dev/null +++ b/roles/lib_openshift/src/test/integration/oc_process.yml @@ -0,0 +1,83 @@ +#!/usr/bin/ansible-playbook --module-path=../../../library/:../../../../lib_utils/library + +--- +- hosts: "{{ cli_master_test }}" + gather_facts: no + user: root + vars: + template_name: mysql-ephemeral + ns_name: test + + post_tasks: + - name: get the mysql-ephemeral template + oc_obj: + name: mysql-ephemeral + state: list + namespace: openshift + kind: template + register: mysqltempl + + - name: fix namespace + yedit: + src: /tmp/mysql-template + key: metadata.namespace + value: test + backup: false + content: "{{ mysqltempl.results.results[0] | to_yaml }}" + + - name: create the test namespace + oc_obj: + name: test + state: present + namespace: test + kind: namespace + content: + path: /tmp/ns_test + data: + apiVersion: v1 + kind: Namespace + metadata: + name: test + spec: + finalizers: + - openshift.io/origin + - kubernetes + register: mysqltempl + + - name: create the mysql-ephemeral template + oc_obj: + name: mysql-ephemeral + state: present + namespace: test + kind: template + files: + - /tmp/mysql-template + delete_after: True + register: mysqltempl + + - name: process mysql-ephemeral + oc_process: + template_name: mysql-ephemeral + namespace: test + params: + NAMESPACE: test + DATABASE_SERVICE_NAME: testdb + create: False + reconcile: false + register: procout + + - assert: + that: + - not procout.changed + - procout.results.results['items'][0]['metadata']['name'] == 'testdb' + - procout.results.results['items'][0]['kind'] == 'Service' + - procout.results.results['items'][1]['metadata']['name'] == 'testdb' + - procout.results.results['items'][1]['kind'] == 'DeploymentConfig' + msg: process failed on template + + - name: remove namespace test + oc_obj: + kind: namespace + name: test + namespace: test + state: absent diff --git a/roles/lib_openshift/src/test/unit/oc_process.py b/roles/lib_openshift/src/test/unit/oc_process.py new file mode 100755 index 000000000..c0cf625cc --- /dev/null +++ b/roles/lib_openshift/src/test/unit/oc_process.py @@ -0,0 +1,473 @@ +#!/usr/bin/env python2 +''' + Unit tests for oc process +''' +# To run +# python -m unittest version +# +# . +# Ran 1 test in 0.597s +# +# 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 +# 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_process import OCProcess # noqa: E402 + + +# pylint: disable=too-many-public-methods +class OCProcessTest(unittest.TestCase): + ''' + Test class for OCProcess + ''' + mysql = '''{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "mysql-ephemeral", + "namespace": "openshift", + "selfLink": "/oapi/v1/namespaces/openshift/templates/mysql-ephemeral", + "uid": "fb8b5f04-e3d3-11e6-a982-0e84250fc302", + "resourceVersion": "480", + "creationTimestamp": "2017-01-26T14:30:27Z", + "annotations": { + "iconClass": "icon-mysql-database", + "openshift.io/display-name": "MySQL (Ephemeral)", + "tags": "database,mysql" + } + }, + "objects": [ + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "creationTimestamp": null, + "name": "${DATABASE_SERVICE_NAME}" + }, + "spec": { + "ports": [ + { + "name": "mysql", + "nodePort": 0, + "port": 3306, + "protocol": "TCP", + "targetPort": 3306 + } + ], + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + }, + { + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": null, + "name": "${DATABASE_SERVICE_NAME}" + }, + "spec": { + "replicas": 1, + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "strategy": { + "type": "Recreate" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "name": "${DATABASE_SERVICE_NAME}" + } + }, + "spec": { + "containers": [ + { + "capabilities": {}, + "env": [ + { + "name": "MYSQL_USER", + "value": "${MYSQL_USER}" + }, + { + "name": "MYSQL_PASSWORD", + "value": "${MYSQL_PASSWORD}" + }, + { + "name": "MYSQL_DATABASE", + "value": "${MYSQL_DATABASE}" + } + ], + "image": " ", + "imagePullPolicy": "IfNotPresent", + "livenessProbe": { + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + }, + "timeoutSeconds": 1 + }, + "name": "mysql", + "ports": [ + { + "containerPort": 3306, + "protocol": "TCP" + } + ], + "readinessProbe": { + "exec": { + "command": [ + "/bin/sh", + "-i", + "-c", + "MYSQL_PWD=$MYSQL_PASSWORD mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'" + ] + }, + "initialDelaySeconds": 5, + "timeoutSeconds": 1 + }, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "securityContext": { + "capabilities": {}, + "privileged": false + }, + "terminationMessagePath": "/dev/termination-log", + "volumeMounts": [ + { + "mountPath": "/var/lib/mysql/data", + "name": "${DATABASE_SERVICE_NAME}-data" + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "volumes": [ + { + "emptyDir": { + "medium": "" + }, + "name": "${DATABASE_SERVICE_NAME}-data" + } + ] + } + }, + "triggers": [ + { + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "mysql" + ], + "from": { + "kind": "ImageStreamTag", + "name": "mysql:${MYSQL_VERSION}", + "namespace": "${NAMESPACE}" + }, + "lastTriggeredImage": "" + }, + "type": "ImageChange" + }, + { + "type": "ConfigChange" + } + ] + }, + "status": {} + } + ], + "parameters": [ + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "DATABASE_SERVICE_NAME", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", + "value": "mysql", + "required": true + }, + { + "name": "MYSQL_USER", + "displayName": "MySQL Connection Username", + "description": "Username for MySQL user that will be used for accessing the database.", + "generate": "expression", + "from": "user[A-Z0-9]{3}", + "required": true + }, + { + "name": "MYSQL_PASSWORD", + "displayName": "MySQL Connection Password", + "description": "Password for the MySQL connection user.", + "generate": "expression", + "from": "[a-zA-Z0-9]{16}", + "required": true + }, + { + "name": "MYSQL_DATABASE", + "displayName": "MySQL Database Name", + "description": "Name of the MySQL database accessed.", + "value": "sampledb", + "required": true + }, + { + "name": "MYSQL_VERSION", + "displayName": "Version of MySQL Image", + "description": "Version of MySQL image to be used (5.5, 5.6 or latest).", + "value": "5.6", + "required": true + } + ], + "labels": { + "template": "mysql-ephemeral-template" + } +}''' + + def setUp(self): + ''' setup method will set to known configuration ''' + pass + + @mock.patch('oc_process.OCProcess._run') + def test_state_list(self, mock_cmd): + ''' Testing a get ''' + params = {'template_name': 'mysql-ephermeral', + 'namespace': 'test', + 'content': None, + 'state': 'list', + 'reconcile': False, + 'create': False, + 'params': {'NAMESPACE': 'test', 'DATABASE_SERVICE_NAME': 'testdb'}, + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False} + + mock_cmd.side_effect = [ + (0, OCProcessTest.mysql, '') + ] + + results = OCProcess.run_ansible(params, False) + + self.assertFalse(results['changed']) + self.assertEqual(results['results']['results'][0]['metadata']['name'], 'mysql-ephemeral') + + @mock.patch('oc_process.OCProcess._run') + def test_process_no_create(self, mock_cmd): + ''' Testing a process with no create ''' + params = {'template_name': 'mysql-ephermeral', + 'namespace': 'test', + 'content': None, + 'state': 'present', + 'reconcile': False, + 'create': False, + 'params': {'NAMESPACE': 'test', 'DATABASE_SERVICE_NAME': 'testdb'}, + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False} + + mysqlproc = '''{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "creationTimestamp": null, + "labels": { + "template": "mysql-ephemeral-template" + }, + "name": "testdb" + }, + "spec": { + "ports": [ + { + "name": "mysql", + "nodePort": 0, + "port": 3306, + "protocol": "TCP", + "targetPort": 3306 + } + ], + "selector": { + "name": "testdb" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + }, + { + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": null, + "labels": { + "template": "mysql-ephemeral-template" + }, + "name": "testdb" + }, + "spec": { + "replicas": 1, + "selector": { + "name": "testdb" + }, + "strategy": { + "type": "Recreate" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "name": "testdb" + } + }, + "spec": { + "containers": [ + { + "capabilities": {}, + "env": [ + { + "name": "MYSQL_USER", + "value": "userHJJ" + }, + { + "name": "MYSQL_PASSWORD", + "value": "GITOAduAMaV6k688" + }, + { + "name": "MYSQL_DATABASE", + "value": "sampledb" + } + ], + "image": " ", + "imagePullPolicy": "IfNotPresent", + "livenessProbe": { + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + }, + "timeoutSeconds": 1 + }, + "name": "mysql", + "ports": [ + { + "containerPort": 3306, + "protocol": "TCP" + } + ], + "readinessProbe": { + "exec": { + "command": [ + "/bin/sh", + "-i", + "-c", + "MYSQL_PWD=$MYSQL_PASSWORD mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'" + ] + }, + "initialDelaySeconds": 5, + "timeoutSeconds": 1 + }, + "resources": { + "limits": { + "memory": "512Mi" + } + }, + "securityContext": { + "capabilities": {}, + "privileged": false + }, + "terminationMessagePath": "/dev/termination-log", + "volumeMounts": [ + { + "mountPath": "/var/lib/mysql/data", + "name": "testdb-data" + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "volumes": [ + { + "emptyDir": { + "medium": "" + }, + "name": "testdb-data" + } + ] + } + }, + "triggers": [ + { + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "mysql" + ], + "from": { + "kind": "ImageStreamTag", + "name": "mysql:5.6", + "namespace": "test" + }, + "lastTriggeredImage": "" + }, + "type": "ImageChange" + }, + { + "type": "ConfigChange" + } + ] + } + } + ] +}''' + + mock_cmd.side_effect = [ + (0, OCProcessTest.mysql, ''), + (0, OCProcessTest.mysql, ''), + (0, mysqlproc, ''), + ] + + results = OCProcess.run_ansible(params, False) + + self.assertFalse(results['changed']) + self.assertEqual(results['results']['results']['items'][0]['metadata']['name'], 'testdb') + + def tearDown(self): + '''TearDown method''' + pass + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3