summaryrefslogtreecommitdiffstats
path: root/roles/lib_openshift/src
diff options
context:
space:
mode:
authorOpenShift Bot <eparis+openshiftbot@redhat.com>2017-03-24 12:38:47 -0500
committerGitHub <noreply@github.com>2017-03-24 12:38:47 -0500
commit86d56d1aabdff4a4d828a3f22099640f9769f9f4 (patch)
tree2b64a3dda18e00f8d793220de443abff43933b27 /roles/lib_openshift/src
parent4444a6ff2b1885b35a190e1f5e9a0f3a26ad4dfb (diff)
parent3d484dd5e5ac4a8ba0c4b50baba79d6d7e5bba7e (diff)
downloadopenshift-86d56d1aabdff4a4d828a3f22099640f9769f9f4.tar.gz
openshift-86d56d1aabdff4a4d828a3f22099640f9769f9f4.tar.bz2
openshift-86d56d1aabdff4a4d828a3f22099640f9769f9f4.tar.xz
openshift-86d56d1aabdff4a4d828a3f22099640f9769f9f4.zip
Merge pull request #3721 from kwoodson/oc_configmap
Merged by openshift-bot
Diffstat (limited to 'roles/lib_openshift/src')
-rw-r--r--roles/lib_openshift/src/ansible/oc_configmap.py32
-rw-r--r--roles/lib_openshift/src/class/oc_configmap.py187
-rw-r--r--roles/lib_openshift/src/doc/configmap72
-rw-r--r--roles/lib_openshift/src/sources.yml10
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_configmap.yml95
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_configmap.py239
6 files changed, 635 insertions, 0 deletions
diff --git a/roles/lib_openshift/src/ansible/oc_configmap.py b/roles/lib_openshift/src/ansible/oc_configmap.py
new file mode 100644
index 000000000..974f72499
--- /dev/null
+++ b/roles/lib_openshift/src/ansible/oc_configmap.py
@@ -0,0 +1,32 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+def main():
+ '''
+ ansible oc module for managing OpenShift configmap objects
+ '''
+
+ 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'),
+ namespace=dict(default='default', type='str'),
+ name=dict(default=None, required=True, type='str'),
+ from_file=dict(default=None, type='dict'),
+ from_literal=dict(default=None, type='dict'),
+ ),
+ supports_check_mode=True,
+ )
+
+
+ rval = OCConfigMap.run_ansible(module.params, module.check_mode)
+ if 'failed' in rval:
+ module.fail_json(**rval)
+
+ module.exit_json(**rval)
+
+if __name__ == '__main__':
+ main()
diff --git a/roles/lib_openshift/src/class/oc_configmap.py b/roles/lib_openshift/src/class/oc_configmap.py
new file mode 100644
index 000000000..87de3e1df
--- /dev/null
+++ b/roles/lib_openshift/src/class/oc_configmap.py
@@ -0,0 +1,187 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+# pylint: disable=too-many-arguments
+class OCConfigMap(OpenShiftCLI):
+ ''' Openshift ConfigMap Class
+
+ ConfigMaps are a way to store data inside of objects
+ '''
+ def __init__(self,
+ name,
+ from_file,
+ from_literal,
+ state,
+ namespace,
+ kubeconfig='/etc/origin/master/admin.kubeconfig',
+ verbose=False):
+ ''' Constructor for OpenshiftOC '''
+ super(OCConfigMap, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose)
+ self.name = name
+ self.state = state
+ self._configmap = None
+ self._inc_configmap = None
+ self.from_file = from_file if from_file is not None else {}
+ self.from_literal = from_literal if from_literal is not None else {}
+
+ @property
+ def configmap(self):
+ if self._configmap is None:
+ self._configmap = self.get()
+
+ return self._configmap
+
+ @configmap.setter
+ def configmap(self, inc_map):
+ self._configmap = inc_map
+
+ @property
+ def inc_configmap(self):
+ if self._inc_configmap is None:
+ results = self.create(dryrun=True, output=True)
+ self._inc_configmap = results['results']
+
+ return self._inc_configmap
+
+ @inc_configmap.setter
+ def inc_configmap(self, inc_map):
+ self._inc_configmap = inc_map
+
+ def from_file_to_params(self):
+ '''return from_files in a string ready for cli'''
+ return ["--from-file={}={}".format(key, value) for key, value in self.from_file.items()]
+
+ def from_literal_to_params(self):
+ '''return from_literal in a string ready for cli'''
+ return ["--from-literal={}={}".format(key, value) for key, value in self.from_literal.items()]
+
+ def get(self):
+ '''return a configmap by name '''
+ results = self._get('configmap', self.name)
+ if results['returncode'] == 0 and results['results'][0]:
+ self.configmap = results['results'][0]
+
+ if results['returncode'] != 0 and '"{}" not found'.format(self.name) in results['stderr']:
+ results['returncode'] = 0
+
+ return results
+
+ def delete(self):
+ '''delete a configmap by name'''
+ return self._delete('configmap', self.name)
+
+ def create(self, dryrun=False, output=False):
+ '''Create a configmap
+
+ :dryrun: Product what you would have done. default: False
+ :output: Whether to parse output. default: False
+ '''
+
+ cmd = ['create', 'configmap', self.name]
+ if self.from_literal is not None:
+ cmd.extend(self.from_literal_to_params())
+
+ if self.from_file is not None:
+ cmd.extend(self.from_file_to_params())
+
+ if dryrun:
+ cmd.extend(['--dry-run', '-ojson'])
+
+ results = self.openshift_cmd(cmd, output=output)
+
+ return results
+
+ def update(self):
+ '''run update configmap '''
+ return self._replace_content('configmap', self.name, self.inc_configmap)
+
+ def needs_update(self):
+ '''compare the current configmap with the proposed and return if they are equal'''
+ return not Utils.check_def_equal(self.inc_configmap, self.configmap, debug=self.verbose)
+
+ @staticmethod
+ # pylint: disable=too-many-return-statements,too-many-branches
+ # TODO: This function should be refactored into its individual parts.
+ def run_ansible(params, check_mode):
+ '''run the ansible idempotent code'''
+
+ oc_cm = OCConfigMap(params['name'],
+ params['from_file'],
+ params['from_literal'],
+ params['state'],
+ params['namespace'],
+ kubeconfig=params['kubeconfig'],
+ verbose=params['debug'])
+
+ state = params['state']
+
+ api_rval = oc_cm.get()
+
+ if 'failed' in api_rval:
+ return {'failed': True, 'msg': api_rval}
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ return {'changed': False, 'results': api_rval, 'state': state}
+
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ if not Utils.exists(api_rval['results'], params['name']):
+ return {'changed': False, 'state': 'absent'}
+
+ if check_mode:
+ return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'}
+
+ api_rval = oc_cm.delete()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ return {'changed': True, 'results': api_rval, 'state': state}
+
+ ########
+ # Create
+ ########
+ if state == 'present':
+ if not Utils.exists(api_rval['results'], params['name']):
+
+ if check_mode:
+ return {'changed': True, 'msg': 'Would have performed a create.'}
+
+ api_rval = oc_cm.create()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ api_rval = oc_cm.get()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ return {'changed': True, 'results': api_rval, 'state': state}
+
+ ########
+ # Update
+ ########
+ if oc_cm.needs_update():
+
+ api_rval = oc_cm.update()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ api_rval = oc_cm.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, 'msg': 'Unknown state passed. {}'.format(state)}
diff --git a/roles/lib_openshift/src/doc/configmap b/roles/lib_openshift/src/doc/configmap
new file mode 100644
index 000000000..5ca8292c4
--- /dev/null
+++ b/roles/lib_openshift/src/doc/configmap
@@ -0,0 +1,72 @@
+# flake8: noqa
+# pylint: skip-file
+
+DOCUMENTATION = '''
+---
+module: oc_configmap
+short_description: Modify, and idempotently manage openshift configmaps
+description:
+ - Modify openshift configmaps 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 configmap
+ - absent - will remove the configmap
+ 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: True
+ default: None
+ aliases: []
+ namespace:
+ description:
+ - The namespace where the object lives.
+ required: false
+ default: default
+ aliases: []
+ from_file:
+ description:
+ - A dict of key, value pairs representing the configmap key and the value represents the file path.
+ required: false
+ default: None
+ aliases: []
+ from_literal:
+ description:
+ - A dict of key, value pairs representing the configmap key and the value represents the string content
+ required: false
+ default: None
+ aliases: []
+author:
+- "kenny woodson <kwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+- name: create group
+ oc_configmap:
+ state: present
+ name: testmap
+ from_file:
+ secret: /path/to/secret
+ from_literal:
+ title: systemadmin
+ register: configout
+'''
diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml
index 0dba6016b..86e243e89 100644
--- a/roles/lib_openshift/src/sources.yml
+++ b/roles/lib_openshift/src/sources.yml
@@ -79,6 +79,16 @@ oc_atomic_container.py:
- doc/atomic_container
- ansible/oc_atomic_container.py
+oc_configmap.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/configmap
+- ../../lib_utils/src/class/yedit.py
+- lib/base.py
+- class/oc_configmap.py
+- ansible/oc_configmap.py
+
oc_edit.py:
- doc/generated
- doc/license
diff --git a/roles/lib_openshift/src/test/integration/oc_configmap.yml b/roles/lib_openshift/src/test/integration/oc_configmap.yml
new file mode 100755
index 000000000..c0d200e73
--- /dev/null
+++ b/roles/lib_openshift/src/test/integration/oc_configmap.yml
@@ -0,0 +1,95 @@
+#!/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
+ vars:
+ filename: /tmp/test_configmap_from_file
+
+ post_tasks:
+ - name: Setup a file with known contents
+ copy:
+ content: This is a file
+ dest: "{{ filename }}"
+
+ - name: create a test project
+ oc_project:
+ name: test
+ description: for tests only
+
+ ###### create test ###########
+ - name: create a configmap
+ oc_configmap:
+ state: present
+ name: configmaptest
+ namespace: test
+ from_file:
+ config: "{{ filename }}"
+ from_literal:
+ foo: bar
+
+ - name: fetch the created configmap
+ oc_configmap:
+ name: configmaptest
+ state: list
+ namespace: test
+ register: cmout
+
+ - debug: var=cmout
+
+ - name: assert configmaptest exists
+ assert:
+ that:
+ - cmout.results.results[0].metadata.name == 'configmaptest'
+ - cmout.results.results[0].data.foo == 'bar'
+ ###### end create test ###########
+
+ ###### update test ###########
+ - name: create a configmap
+ oc_configmap:
+ state: present
+ name: configmaptest
+ namespace: test
+ from_file:
+ config: "{{ filename }}"
+ from_literal:
+ foo: notbar
+ deployment_type: online
+
+ - name: fetch the updated configmap
+ oc_configmap:
+ name: configmaptest
+ state: list
+ namespace: test
+ register: cmout
+
+ - debug: var=cmout
+
+ - name: assert configmaptest exists
+ assert:
+ that:
+ - cmout.results.results[0].metadata.name == 'configmaptest'
+ - cmout.results.results[0].data.deployment_type == 'online'
+ - cmout.results.results[0].data.foo == 'notbar'
+ ###### end update test ###########
+
+ ###### delete test ###########
+ - name: delete a configmap
+ oc_configmap:
+ state: absent
+ name: configmaptest
+ namespace: test
+
+ - name: fetch the updated configmap
+ oc_configmap:
+ name: configmaptest
+ state: list
+ namespace: test
+ register: cmout
+
+ - debug: var=cmout
+
+ - name: assert configmaptest exists
+ assert:
+ that: "'\"configmaptest\" not found' in cmout.results.stderr"
diff --git a/roles/lib_openshift/src/test/unit/test_oc_configmap.py b/roles/lib_openshift/src/test/unit/test_oc_configmap.py
new file mode 100755
index 000000000..318fd6167
--- /dev/null
+++ b/roles/lib_openshift/src/test/unit/test_oc_configmap.py
@@ -0,0 +1,239 @@
+'''
+ Unit tests for oc configmap
+'''
+
+import copy
+import os
+import six
+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_configmap import OCConfigMap, locate_oc_binary # noqa: E402
+
+
+class OCConfigMapTest(unittest.TestCase):
+ '''
+ Test class for OCConfigMap
+ '''
+ params = {'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+ 'state': 'present',
+ 'debug': False,
+ 'name': 'configmap',
+ 'from_file': {},
+ 'from_literal': {},
+ 'namespace': 'test'}
+
+ @mock.patch('oc_configmap.Utils._write')
+ @mock.patch('oc_configmap.Utils.create_tmpfile_copy')
+ @mock.patch('oc_configmap.OCConfigMap._run')
+ def test_create_configmap(self, mock_run, mock_tmpfile_copy, mock_write):
+ ''' Testing a configmap create '''
+ # TODO
+ return
+ params = copy.deepcopy(OCConfigMapTest.params)
+ params['from_file'] = {'test': '/root/file'}
+ params['from_literal'] = {'foo': 'bar'}
+
+ configmap = '''{
+ "apiVersion": "v1",
+ "data": {
+ "foo": "bar",
+ "test": "this is a file\\n"
+ },
+ "kind": "ConfigMap",
+ "metadata": {
+ "creationTimestamp": "2017-03-20T20:24:35Z",
+ "name": "configmap",
+ "namespace": "test"
+ }
+ }'''
+
+ mock_run.side_effect = [
+ (1, '', 'Error from server (NotFound): configmaps "configmap" not found'),
+ (0, '', ''),
+ (0, configmap, ''),
+ ]
+
+ mock_tmpfile_copy.side_effect = [
+ '/tmp/mocked_kubeconfig',
+ ]
+
+ results = OCConfigMap.run_ansible(params, False)
+
+ self.assertTrue(results['changed'])
+ self.assertEqual(results['results']['results'][0]['metadata']['name'], 'configmap')
+
+ @mock.patch('oc_configmap.Utils._write')
+ @mock.patch('oc_configmap.Utils.create_tmpfile_copy')
+ @mock.patch('oc_configmap.OCConfigMap._run')
+ def test_update_configmap(self, mock_run, mock_tmpfile_copy, mock_write):
+ ''' Testing a configmap create '''
+ params = copy.deepcopy(OCConfigMapTest.params)
+ params['from_file'] = {'test': '/root/file'}
+ params['from_literal'] = {'foo': 'bar', 'deployment_type': 'online'}
+
+ configmap = '''{
+ "apiVersion": "v1",
+ "data": {
+ "foo": "bar",
+ "test": "this is a file\\n"
+ },
+ "kind": "ConfigMap",
+ "metadata": {
+ "creationTimestamp": "2017-03-20T20:24:35Z",
+ "name": "configmap",
+ "namespace": "test"
+
+ }
+ }'''
+
+ mod_configmap = '''{
+ "apiVersion": "v1",
+ "data": {
+ "foo": "bar",
+ "deployment_type": "online",
+ "test": "this is a file\\n"
+ },
+ "kind": "ConfigMap",
+ "metadata": {
+ "creationTimestamp": "2017-03-20T20:24:35Z",
+ "name": "configmap",
+ "namespace": "test"
+
+ }
+ }'''
+
+ mock_run.side_effect = [
+ (0, configmap, ''),
+ (0, mod_configmap, ''),
+ (0, configmap, ''),
+ (0, '', ''),
+ (0, mod_configmap, ''),
+ ]
+
+ mock_tmpfile_copy.side_effect = [
+ '/tmp/mocked_kubeconfig',
+ ]
+
+ results = OCConfigMap.run_ansible(params, False)
+
+ self.assertTrue(results['changed'])
+ self.assertEqual(results['results']['results'][0]['metadata']['name'], 'configmap')
+ self.assertEqual(results['results']['results'][0]['data']['deployment_type'], 'online')
+
+ @unittest.skipIf(six.PY3, 'py2 test only')
+ @mock.patch('os.path.exists')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_fallback(self, mock_env_get, mock_path_exists):
+ ''' Testing binary lookup fallback '''
+
+ mock_env_get.side_effect = lambda _v, _d: ''
+
+ mock_path_exists.side_effect = lambda _: False
+
+ self.assertEqual(locate_oc_binary(), 'oc')
+
+ @unittest.skipIf(six.PY3, 'py2 test only')
+ @mock.patch('os.path.exists')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_path(self, mock_env_get, mock_path_exists):
+ ''' Testing binary lookup in path '''
+
+ oc_bin = '/usr/bin/oc'
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_path_exists.side_effect = lambda f: f == oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @unittest.skipIf(six.PY3, 'py2 test only')
+ @mock.patch('os.path.exists')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_usr_local(self, mock_env_get, mock_path_exists):
+ ''' Testing binary lookup in /usr/local/bin '''
+
+ oc_bin = '/usr/local/bin/oc'
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_path_exists.side_effect = lambda f: f == oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @unittest.skipIf(six.PY3, 'py2 test only')
+ @mock.patch('os.path.exists')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_home(self, mock_env_get, mock_path_exists):
+ ''' Testing binary lookup in ~/bin '''
+
+ oc_bin = os.path.expanduser('~/bin/oc')
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_path_exists.side_effect = lambda f: f == oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @unittest.skipIf(six.PY2, 'py3 test only')
+ @mock.patch('shutil.which')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_fallback_py3(self, mock_env_get, mock_shutil_which):
+ ''' Testing binary lookup fallback '''
+
+ mock_env_get.side_effect = lambda _v, _d: ''
+
+ mock_shutil_which.side_effect = lambda _f, path=None: None
+
+ self.assertEqual(locate_oc_binary(), 'oc')
+
+ @unittest.skipIf(six.PY2, 'py3 test only')
+ @mock.patch('shutil.which')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_path_py3(self, mock_env_get, mock_shutil_which):
+ ''' Testing binary lookup in path '''
+
+ oc_bin = '/usr/bin/oc'
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_shutil_which.side_effect = lambda _f, path=None: oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @unittest.skipIf(six.PY2, 'py3 test only')
+ @mock.patch('shutil.which')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_usr_local_py3(self, mock_env_get, mock_shutil_which):
+ ''' Testing binary lookup in /usr/local/bin '''
+
+ oc_bin = '/usr/local/bin/oc'
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_shutil_which.side_effect = lambda _f, path=None: oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @unittest.skipIf(six.PY2, 'py3 test only')
+ @mock.patch('shutil.which')
+ @mock.patch('os.environ.get')
+ def test_binary_lookup_in_home_py3(self, mock_env_get, mock_shutil_which):
+ ''' Testing binary lookup in ~/bin '''
+
+ oc_bin = os.path.expanduser('~/bin/oc')
+
+ mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin'
+
+ mock_shutil_which.side_effect = lambda _f, path=None: oc_bin
+
+ self.assertEqual(locate_oc_binary(), oc_bin)