summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--roles/lib_openshift_api/build/ansible/obj.py132
-rw-r--r--roles/lib_openshift_api/build/ansible/secret.py121
-rwxr-xr-xroles/lib_openshift_api/build/generate.py53
-rw-r--r--roles/lib_openshift_api/build/src/base.py (renamed from roles/lib_openshift_api/library/oc_service.py)194
-rw-r--r--roles/lib_openshift_api/build/src/obj.py78
-rw-r--r--roles/lib_openshift_api/build/src/secret.py68
-rw-r--r--roles/lib_openshift_api/build/test/README5
-rwxr-xr-xroles/lib_openshift_api/build/test/deploymentconfig.yml120
-rw-r--r--roles/lib_openshift_api/build/test/files/config.yml1
-rw-r--r--roles/lib_openshift_api/build/test/files/dc-mod.yml124
-rw-r--r--roles/lib_openshift_api/build/test/files/dc.yml121
-rw-r--r--roles/lib_openshift_api/build/test/files/passwords.yml4
-rw-r--r--roles/lib_openshift_api/build/test/files/router-mod.json30
-rw-r--r--roles/lib_openshift_api/build/test/files/router.json29
l---------roles/lib_openshift_api/build/test/roles1
-rwxr-xr-xroles/lib_openshift_api/build/test/secrets.yml81
-rwxr-xr-xroles/lib_openshift_api/build/test/services.yml133
-rw-r--r--roles/lib_openshift_api/library/oc_obj.py (renamed from roles/lib_openshift_api/library/oc_deploymentconfig.py)349
-rw-r--r--roles/lib_openshift_api/library/oc_secret.py231
-rw-r--r--roles/lib_yaml_editor/build/ansible/yedit.py66
-rwxr-xr-xroles/lib_yaml_editor/build/generate.py42
-rw-r--r--roles/lib_yaml_editor/build/src/base.py9
-rw-r--r--roles/lib_yaml_editor/build/src/yedit.py160
-rw-r--r--roles/lib_yaml_editor/build/test/foo.yml1
-rwxr-xr-xroles/lib_yaml_editor/build/test/test.yaml15
-rw-r--r--roles/lib_yaml_editor/library/yedit.py42
-rw-r--r--test/env-setup2
-rwxr-xr-xtest/units/yedit_test.py167
28 files changed, 1969 insertions, 410 deletions
diff --git a/roles/lib_openshift_api/build/ansible/obj.py b/roles/lib_openshift_api/build/ansible/obj.py
new file mode 100644
index 000000000..0796d807e
--- /dev/null
+++ b/roles/lib_openshift_api/build/ansible/obj.py
@@ -0,0 +1,132 @@
+# pylint: skip-file
+
+# pylint: disable=too-many-branches
+def main():
+ '''
+ ansible oc module for services
+ '''
+
+ 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, type='str'),
+ files=dict(default=None, type='list'),
+ kind=dict(required=True,
+ type='str',
+ choices=['dc', 'deploymentconfig',
+ 'svc', 'service',
+ 'secret',
+ ]),
+ delete_after=dict(default=False, type='bool'),
+ content=dict(default=None, type='dict'),
+ force=dict(default=False, type='bool'),
+ ),
+ mutually_exclusive=[["content", "files"]],
+
+ supports_check_mode=True,
+ )
+ ocobj = OCObject(module.params['kind'],
+ module.params['namespace'],
+ module.params['name'],
+ kubeconfig=module.params['kubeconfig'],
+ verbose=module.params['debug'])
+
+ state = module.params['state']
+
+ api_rval = ocobj.get()
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ module.exit_json(changed=False, results=api_rval['results'], state="list")
+
+ if not module.params['name']:
+ module.fail_json(msg='Please specify a name when state is absent|present.')
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ if not Utils.exists(api_rval['results'], module.params['name']):
+ module.exit_json(changed=False, state="absent")
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed a delete.')
+
+ api_rval = ocobj.delete()
+ module.exit_json(changed=True, results=api_rval, state="absent")
+
+ if state == 'present':
+ ########
+ # Create
+ ########
+ if not Utils.exists(api_rval['results'], module.params['name']):
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed a create.')
+
+ # Create it here
+ api_rval = ocobj.create(module.params['files'], module.params['content'])
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
+
+ # return the created object
+ api_rval = ocobj.get()
+
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
+
+ # Remove files
+ if module.params['files'] and module.params['delete_after']:
+ Utils.cleanup(module.params['files'])
+
+ module.exit_json(changed=True, results=api_rval, state="present")
+
+ ########
+ # Update
+ ########
+ # if a file path is passed, use it.
+ update = ocobj.needs_update(module.params['files'], module.params['content'])
+ if not isinstance(update, bool):
+ module.fail_json(msg=update)
+
+ # No changes
+ if not update:
+ if module.params['files'] and module.params['delete_after']:
+ Utils.cleanup(module.params['files'])
+
+ module.exit_json(changed=False, results=api_rval['results'][0], state="present")
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed an update.')
+
+ api_rval = ocobj.update(module.params['files'],
+ module.params['content'],
+ module.params['force'])
+
+
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
+
+ # return the created object
+ api_rval = ocobj.get()
+
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
+
+ module.exit_json(changed=True, results=api_rval, state="present")
+
+ module.exit_json(failed=True,
+ changed=False,
+ results='Unknown state passed. %s' % state,
+ state="unknown")
+
+# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
+# import module snippets. This are required
+from ansible.module_utils.basic import *
+
+main()
diff --git a/roles/lib_openshift_api/build/ansible/secret.py b/roles/lib_openshift_api/build/ansible/secret.py
new file mode 100644
index 000000000..8df7bbc64
--- /dev/null
+++ b/roles/lib_openshift_api/build/ansible/secret.py
@@ -0,0 +1,121 @@
+# pylint: skip-file
+
+# pylint: disable=too-many-branches
+def main():
+ '''
+ ansible oc module for secrets
+ '''
+
+ 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, type='str'),
+ files=dict(default=None, type='list'),
+ delete_after=dict(default=False, type='bool'),
+ contents=dict(default=None, type='list'),
+ force=dict(default=False, type='bool'),
+ ),
+ mutually_exclusive=[["contents", "files"]],
+
+ supports_check_mode=True,
+ )
+ occmd = Secret(module.params['namespace'],
+ module.params['name'],
+ kubeconfig=module.params['kubeconfig'],
+ verbose=module.params['debug'])
+
+ state = module.params['state']
+
+ api_rval = occmd.get()
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ module.exit_json(changed=False, results=api_rval['results'], state="list")
+
+ if not module.params['name']:
+ module.fail_json(msg='Please specify a name when state is absent|present.')
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ if not Utils.exists(api_rval['results'], module.params['name']):
+ module.exit_json(changed=False, state="absent")
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed a delete.')
+
+ api_rval = occmd.delete()
+ module.exit_json(changed=True, results=api_rval, state="absent")
+
+
+ if state == 'present':
+ if module.params['files']:
+ files = module.params['files']
+ elif module.params['contents']:
+ files = Utils.create_files_from_contents(module.params['contents'])
+ else:
+ module.fail_json(msg='Either specify files or contents.')
+
+ ########
+ # Create
+ ########
+ if not Utils.exists(api_rval['results'], module.params['name']):
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed a create.')
+
+ api_rval = occmd.create(module.params['files'], module.params['contents'])
+
+ # Remove files
+ if files and module.params['delete_after']:
+ Utils.cleanup(files)
+
+ module.exit_json(changed=True, results=api_rval, state="present")
+
+ ########
+ # Update
+ ########
+ secret = occmd.prep_secret(module.params['files'], module.params['contents'])
+
+ if secret['returncode'] != 0:
+ module.fail_json(msg=secret)
+
+ if Utils.check_def_equal(secret['results'], api_rval['results'][0]):
+
+ # Remove files
+ if files and module.params['delete_after']:
+ Utils.cleanup(files)
+
+ module.exit_json(changed=False, results=secret['results'], state="present")
+
+ if module.check_mode:
+ module.exit_json(change=False, msg='Would have performed an update.')
+
+ api_rval = occmd.update(files, force=module.params['force'])
+
+ # Remove files
+ if secret and module.params['delete_after']:
+ Utils.cleanup(files)
+
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
+
+
+ module.exit_json(changed=True, results=api_rval, state="present")
+
+ module.exit_json(failed=True,
+ changed=False,
+ results='Unknown state passed. %s' % state,
+ state="unknown")
+
+# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
+# import module snippets. This are required
+from ansible.module_utils.basic import *
+
+main()
diff --git a/roles/lib_openshift_api/build/generate.py b/roles/lib_openshift_api/build/generate.py
new file mode 100755
index 000000000..ab70dd7f3
--- /dev/null
+++ b/roles/lib_openshift_api/build/generate.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+'''
+ Generate the openshift-ansible/roles/lib_openshift_cli/library/ modules.
+'''
+
+import os
+
+# pylint: disable=anomalous-backslash-in-string
+GEN_STR = "#!usr/bin/env python\n" + \
+ "# ___ ___ _ _ ___ ___ _ _____ ___ ___\n" + \
+ "# / __| __| \| | __| _ \ /_\_ _| __| \\\n" + \
+ "# | (_ | _|| .` | _|| / / _ \| | | _|| |) |\n" + \
+ "# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____\n" + \
+ "# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|\n" + \
+ "# | |) | (_) | | .` | (_) || | | _|| |) | | | |\n" + \
+ "# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|\n"
+
+
+
+FILES = {'oc_obj.py': ['src/base.py',
+ '../../lib_yaml_editor/build/src/yedit.py',
+ 'src/obj.py',
+ 'ansible/obj.py',
+ ],
+ 'oc_secret.py': ['src/base.py',
+ '../../lib_yaml_editor/build/src/yedit.py',
+ 'src/secret.py',
+ 'ansible/secret.py',
+ ],
+ }
+
+
+def main():
+ ''' combine the necessary files to create the ansible module '''
+ openshift_ansible = ('../library/')
+ for fname, parts in FILES.items():
+ with open(os.path.join(openshift_ansible, fname), 'w') as afd:
+ afd.seek(0)
+ afd.write(GEN_STR)
+ for fpart in parts:
+ with open(fpart) as pfd:
+ # first line is pylint disable so skip it
+ for idx, line in enumerate(pfd):
+ if idx == 0 and 'skip-file' in line:
+ continue
+
+ afd.write(line)
+
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/roles/lib_openshift_api/library/oc_service.py b/roles/lib_openshift_api/build/src/base.py
index e7bd2514e..31c102e5d 100644
--- a/roles/lib_openshift_api/library/oc_service.py
+++ b/roles/lib_openshift_api/build/src/base.py
@@ -1,7 +1,8 @@
-#!/usr/bin/env python
+# pylint: skip-file
'''
- OpenShiftCLI class that wraps the oc commands in a subprocess
+ OpenShiftCLI class that wraps the oc commands in a subprocess
'''
+
import atexit
import json
import os
@@ -9,6 +10,7 @@ import shutil
import subprocess
import yaml
+# pylint: disable=too-few-public-methods
class OpenShiftCLI(object):
''' Class to wrap the oc command line tools '''
def __init__(self,
@@ -20,22 +22,39 @@ class OpenShiftCLI(object):
self.verbose = verbose
self.kubeconfig = kubeconfig
- def replace(self, fname, force=False):
+ # Pylint allows only 5 arguments to be passed.
+ # pylint: disable=too-many-arguments
+ def _replace_content(self, resource, rname, content, force=False):
+ ''' replace the current object with the content '''
+ res = self._get(resource, rname)
+ if not res['results']:
+ return res
+
+ fname = '/tmp/%s' % rname
+ yed = Yedit(fname, res['results'][0])
+ for key, value in content.items():
+ yed.put(key, value)
+
+ atexit.register(Utils.cleanup, [fname])
+
+ return self._replace(fname, force)
+
+ def _replace(self, fname, force=False):
'''return all pods '''
- cmd = ['replace', '-f', fname]
+ cmd = ['-n', self.namespace, 'replace', '-f', fname]
if force:
- cmd = ['replace', '--force', '-f', fname]
+ cmd.append('--force')
return self.oc_cmd(cmd)
- def create(self, fname):
+ def _create(self, fname):
'''return all pods '''
return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
- def delete(self, resource, rname):
+ def _delete(self, resource, rname):
'''return all pods '''
return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
- def get(self, resource, rname=None):
+ def _get(self, resource, rname=None):
'''return a secret by name '''
cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
if rname:
@@ -96,7 +115,7 @@ class Utils(object):
path = os.path.join('/tmp', rname)
with open(path, 'w') as fds:
if ftype == 'yaml':
- fds.write(yaml.dump(data, default_flow_style=False))
+ fds.write(yaml.safe_dump(data, default_flow_style=False))
elif ftype == 'json':
fds.write(json.dumps(data))
@@ -173,7 +192,7 @@ class Utils(object):
''' Given a user defined definition, compare it with the results given back by our query. '''
# Currently these values are autogenerated and we do not need to check them
- skip = ['creationTimestamp', 'selfLink', 'resourceVersion', 'uid', 'namespace']
+ skip = ['metadata', 'status']
for key, value in result_def.items():
if key in skip:
@@ -221,158 +240,3 @@ class Utils(object):
return False
return True
-
-class Service(OpenShiftCLI):
- ''' Class to wrap the oc command line tools
- '''
- def __init__(self,
- namespace,
- service_name=None,
- kubeconfig='/etc/origin/master/admin.kubeconfig',
- verbose=False):
- ''' Constructor for OpenshiftOC '''
- super(Service, self).__init__(namespace, kubeconfig)
- self.namespace = namespace
- self.name = service_name
- self.verbose = verbose
- self.kubeconfig = kubeconfig
-
- def create_service(self, sfile):
- ''' create the service '''
- return self.create(sfile)
-
- def get_services(self):
- '''return a secret by name '''
- return self.get('services', self.name)
-
- def delete_service(self):
- '''return all pods '''
- return self.delete('service', self.name)
-
- def update_service(self, sfile, force=False):
- '''run update service
-
- This receives a list of file names and converts it into a secret.
- The secret is then written to disk and passed into the `oc replace` command.
- '''
- return self.replace(sfile, force=force)
-
-
-# pylint: disable=too-many-branches
-def main():
- '''
- ansible oc module for services
- '''
-
- 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, type='str'),
- service_file=dict(default=None, type='str'),
- input_type=dict(default='yaml',
- choices=['json', 'yaml'],
- type='str'),
- delete_after=dict(default=False, type='bool'),
- contents=dict(default=None, type='list'),
- force=dict(default=False, type='bool'),
- ),
- mutually_exclusive=[["contents", "service_file"]],
-
- supports_check_mode=True,
- )
- occmd = Service(module.params['namespace'],
- module.params['name'],
- kubeconfig=module.params['kubeconfig'],
- verbose=module.params['debug'])
-
- state = module.params['state']
-
- api_rval = occmd.get_services()
-
- #####
- # Get
- #####
- if state == 'list':
- module.exit_json(changed=False, results=api_rval['results'], state="list")
-
- if not module.params['name']:
- module.fail_json(msg='Please specify a name when state is absent|present.')
- ########
- # Delete
- ########
- if state == 'absent':
- if not Utils.exists(api_rval['results'], module.params['name']):
- module.exit_json(changed=False, state="absent")
-
- if module.check_mode:
- module.exit_json(change=False, msg='Would have performed a delete.')
-
- api_rval = occmd.delete_service()
- module.exit_json(changed=True, results=api_rval, state="absent")
-
-
- if state == 'present':
- if module.params['service_file']:
- sfile = module.params['service_file']
- elif module.params['contents']:
- sfile = Utils.create_files_from_contents(module.params['contents'])
- else:
- module.fail_json(msg='Either specify files or contents.')
-
- ########
- # Create
- ########
- if not Utils.exists(api_rval['results'], module.params['name']):
-
- if module.check_mode:
- module.exit_json(change=False, msg='Would have performed a create.')
-
- api_rval = occmd.create_service(sfile)
-
- # Remove files
- if sfile and module.params['delete_after']:
- Utils.cleanup([sfile])
-
- module.exit_json(changed=True, results=api_rval, state="present")
-
- ########
- # Update
- ########
- sfile_contents = Utils.get_resource_file(sfile, module.params['input_type'])
- if Utils.check_def_equal(sfile_contents, api_rval['results'][0]):
-
- # Remove files
- if module.params['service_file'] and module.params['delete_after']:
- Utils.cleanup([sfile])
-
- module.exit_json(changed=False, results=api_rval['results'][0], state="present")
-
- if module.check_mode:
- module.exit_json(change=False, msg='Would have performed an update.')
-
- api_rval = occmd.update_service(sfile, force=module.params['force'])
-
- # Remove files
- if sfile and module.params['delete_after']:
- Utils.cleanup([sfile])
-
- if api_rval['returncode'] != 0:
- module.fail_json(msg=api_rval)
-
-
- module.exit_json(changed=True, results=api_rval, state="present")
-
- module.exit_json(failed=True,
- changed=False,
- results='Unknown state passed. %s' % state,
- state="unknown")
-
-# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
-# import module snippets. This are required
-from ansible.module_utils.basic import *
-
-main()
diff --git a/roles/lib_openshift_api/build/src/obj.py b/roles/lib_openshift_api/build/src/obj.py
new file mode 100644
index 000000000..a3ad4b3c4
--- /dev/null
+++ b/roles/lib_openshift_api/build/src/obj.py
@@ -0,0 +1,78 @@
+# pylint: skip-file
+
+class OCObject(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools '''
+
+ # pylint allows 5. we need 6
+ # pylint: disable=too-many-arguments
+ def __init__(self,
+ kind,
+ namespace,
+ rname=None,
+ kubeconfig='/etc/origin/master/admin.kubeconfig',
+ verbose=False):
+ ''' Constructor for OpenshiftOC '''
+ super(OCObject, self).__init__(namespace, kubeconfig)
+ self.kind = kind
+ self.namespace = namespace
+ self.name = rname
+ self.kubeconfig = kubeconfig
+ self.verbose = verbose
+
+ def get(self):
+ '''return a deploymentconfig by name '''
+ return self._get(self.kind, rname=self.name)
+
+ def delete(self):
+ '''return all pods '''
+ return self._delete(self.kind, self.name)
+
+ def create(self, files=None, content=None):
+ '''Create a deploymentconfig '''
+ if files:
+ return self._create(files[0])
+
+ return self._create(Utils.create_files_from_contents(content))
+
+
+ # pylint: disable=too-many-function-args
+ def update(self, files=None, content=None, force=False):
+ '''run update dc
+
+ This receives a list of file names and takes the first filename and calls replace.
+ '''
+ if files:
+ return self._replace(files[0], force)
+
+ return self.update_content(content, force)
+
+ def update_content(self, content, force=False):
+ '''update the dc with the content'''
+ return self._replace_content(self.kind, self.name, content, force=force)
+
+ def needs_update(self, files=None, content=None, content_type='yaml'):
+ ''' check to see if we need to update '''
+ objects = self.get()
+ if objects['returncode'] != 0:
+ return objects
+
+ # pylint: disable=no-member
+ data = None
+ if files:
+ data = Utils.get_resource_file(files[0], content_type)
+
+ # if equal then no need. So not equal is True
+ return not Utils.check_def_equal(data, objects['results'][0], True)
+ else:
+ data = content
+
+ for key, value in data.items():
+ if key == 'metadata':
+ continue
+ if not objects['results'][0].has_key(key):
+ return True
+ if value != objects['results'][0][key]:
+ return True
+
+ return False
+
diff --git a/roles/lib_openshift_api/build/src/secret.py b/roles/lib_openshift_api/build/src/secret.py
new file mode 100644
index 000000000..af61dfa01
--- /dev/null
+++ b/roles/lib_openshift_api/build/src/secret.py
@@ -0,0 +1,68 @@
+# pylint: skip-file
+
+class Secret(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools
+ '''
+ def __init__(self,
+ namespace,
+ secret_name=None,
+ kubeconfig='/etc/origin/master/admin.kubeconfig',
+ verbose=False):
+ ''' Constructor for OpenshiftOC '''
+ super(Secret, self).__init__(namespace, kubeconfig)
+ self.namespace = namespace
+ self.name = secret_name
+ self.kubeconfig = kubeconfig
+ self.verbose = verbose
+
+ def get(self):
+ '''return a secret by name '''
+ return self._get('secrets', self.name)
+
+ def delete(self):
+ '''delete a secret by name'''
+ return self._delete('secrets', self.name)
+
+ def create(self, files=None, contents=None):
+ '''Create a secret '''
+ if not files:
+ files = Utils.create_files_from_contents(contents)
+
+ secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files]
+ cmd = ['-n%s' % self.namespace, 'secrets', 'new', self.name]
+ cmd.extend(secrets)
+
+ return self.oc_cmd(cmd)
+
+ def update(self, files, force=False):
+ '''run update secret
+
+ This receives a list of file names and converts it into a secret.
+ The secret is then written to disk and passed into the `oc replace` command.
+ '''
+ secret = self.prep_secret(files)
+ if secret['returncode'] != 0:
+ return secret
+
+ sfile_path = '/tmp/%s' % self.name
+ with open(sfile_path, 'w') as sfd:
+ sfd.write(json.dumps(secret['results']))
+
+ atexit.register(Utils.cleanup, [sfile_path])
+
+ return self._replace(sfile_path, force=force)
+
+ def prep_secret(self, files=None, contents=None):
+ ''' return what the secret would look like if created
+ This is accomplished by passing -ojson. This will most likely change in the future
+ '''
+ if not files:
+ files = Utils.create_files_from_contents(contents)
+
+ secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files]
+ cmd = ['-ojson', '-n%s' % self.namespace, 'secrets', 'new', self.name]
+ cmd.extend(secrets)
+
+ return self.oc_cmd(cmd, output=True)
+
+
diff --git a/roles/lib_openshift_api/build/test/README b/roles/lib_openshift_api/build/test/README
new file mode 100644
index 000000000..af9f05b3d
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/README
@@ -0,0 +1,5 @@
+After generate.py has run, the ansible modules will be placed under ../../../openshift-ansible/roles/lib_openshift_api/library.
+
+
+To run the tests you need to run them like this:
+./services.yml -M ../../library
diff --git a/roles/lib_openshift_api/build/test/deploymentconfig.yml b/roles/lib_openshift_api/build/test/deploymentconfig.yml
new file mode 100755
index 000000000..d041ab22a
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/deploymentconfig.yml
@@ -0,0 +1,120 @@
+#!/usr/bin/ansible-playbook
+---
+- hosts: "oo_clusterid_mwoodson:&oo_version_3:&oo_master_primary"
+ gather_facts: no
+ user: root
+
+ post_tasks:
+ - copy:
+ dest: "/tmp/{{ item }}"
+ src: "files/{{ item }}"
+ with_items:
+ - dc.yml
+
+ - name: list dc
+ oc_obj:
+ kind: dc
+ state: list
+ namespace: default
+ name: router
+ register: dcout
+
+ - debug:
+ var: dcout
+
+ - name: absent dc
+ oc_obj:
+ kind: dc
+ state: absent
+ namespace: default
+ name: router
+ register: dcout
+
+ - debug:
+ var: dcout
+
+ - name: present dc
+ oc_obj:
+ kind: dc
+ state: present
+ namespace: default
+ name: router
+ files:
+ - /tmp/dc.yml
+ register: dcout
+
+ - debug:
+ var: dcout
+
+ - name: dump router
+ oc_obj:
+ kind: dc
+ state: list
+ name: router
+ register: routerout
+
+ - name: write router file
+ copy:
+ dest: /tmp/dc-mod.json
+ content: "{{ routerout.results[0] }}"
+
+ - command: cat /tmp/dc-mod.json
+ register: catout
+
+ - debug:
+ msg: "{{ catout }}"
+
+ - command: "sed -i 's/: 80/: 81/g' /tmp/dc-mod.json"
+ register: catout
+
+ - name: present dc update
+ oc_obj:
+ kind: dc
+ state: present
+ namespace: default
+ name: router
+ files:
+ - /tmp/dc-mod.json
+ delete_after: True
+ register: dcout
+
+ - debug:
+ var: dcout
+
+ - include_vars: "files/dc-mod.yml"
+
+ - name: absent dc
+ oc_obj:
+ kind: dc
+ state: absent
+ namespace: default
+ name: router
+ register: dcout
+
+ - debug:
+ var: dcout
+
+ - name: present dc
+ oc_obj:
+ kind: dc
+ state: present
+ namespace: default
+ name: router
+ files:
+ - /tmp/dc.yml
+ delete_after: True
+ register: dcout
+
+ - name: present dc
+ oc_obj:
+ kind: dc
+ state: present
+ namespace: default
+ name: router
+ content: "{{ dc }}"
+ delete_after: True
+ register: dcout
+
+ - debug:
+ var: dcout
+
diff --git a/roles/lib_openshift_api/build/test/files/config.yml b/roles/lib_openshift_api/build/test/files/config.yml
new file mode 100644
index 000000000..c544c6fd4
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/config.yml
@@ -0,0 +1 @@
+value: True
diff --git a/roles/lib_openshift_api/build/test/files/dc-mod.yml b/roles/lib_openshift_api/build/test/files/dc-mod.yml
new file mode 100644
index 000000000..6c700d6c7
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/dc-mod.yml
@@ -0,0 +1,124 @@
+dc:
+ path:
+ dc-mod.yml
+ content:
+ apiVersion: v1
+ kind: DeploymentConfig
+ metadata:
+ labels:
+ router: router
+ name: router
+ namespace: default
+ resourceVersion: "84016"
+ selfLink: /oapi/v1/namespaces/default/deploymentconfigs/router
+ uid: 48f8b9d9-ed42-11e5-9903-0a9a9d4e7f2b
+ spec:
+ replicas: 2
+ selector:
+ router: router
+ strategy:
+ resources: {}
+ rollingParams:
+ intervalSeconds: 1
+ maxSurge: 0
+ maxUnavailable: 25%
+ timeoutSeconds: 600
+ updatePercent: -25
+ updatePeriodSeconds: 1
+ type: Rolling
+ template:
+ metadata:
+ creationTimestamp: null
+ labels:
+ router: router
+ spec:
+ containers:
+ - env:
+ - name: DEFAULT_CERTIFICATE
+ - name: OPENSHIFT_CA_DATA
+ value: |
+ -----BEGIN CERTIFICATE-----
+ MIIC5jCCAdCgAwIBAgIBATALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo
+ -----END CERTIFICATE-----
+ - name: OPENSHIFT_CERT_DATA
+ value: |
+ -----BEGIN CERTIFICATE-----
+ MIIDDTCCAfegAwIBAgIBCDALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo
+ -----END CERTIFICATE-----
+ - name: OPENSHIFT_INSECURE
+ value: "false"
+ - name: OPENSHIFT_KEY_DATA
+ value: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEA2lf49DrPHfCdCORcnIbmDVrx8yos7trjWdBvuledijyslRVR
+ -----END RSA PRIVATE KEY-----
+ - name: OPENSHIFT_MASTER
+ value: https://internal.api.mwoodson.openshift.com
+ - 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_EXTERNAL_HOST_PARTITION_PATH
+ - name: ROUTER_EXTERNAL_HOST_PASSWORD
+ - name: ROUTER_EXTERNAL_HOST_PRIVKEY
+ value: /etc/secret-volume/router.pem
+ - name: ROUTER_EXTERNAL_HOST_USERNAME
+ - name: ROUTER_SERVICE_NAME
+ value: router
+ - name: ROUTER_SERVICE_NAMESPACE
+ value: default
+ - name: STATS_PASSWORD
+ value: ugCk6YBm4q
+ - name: STATS_PORT
+ value: "1936"
+ - name: STATS_USERNAME
+ value: admin
+ image: openshift3/ose-haproxy-router:v3.1.1.6
+ imagePullPolicy: IfNotPresent
+ livenessProbe:
+ httpGet:
+ host: localhost
+ path: /healthz
+ port: 1936
+ scheme: HTTP
+ initialDelaySeconds: 10
+ timeoutSeconds: 1
+ name: router
+ ports:
+ - containerPort: 81
+ hostPort: 81
+ protocol: TCP
+ - containerPort: 443
+ hostPort: 443
+ protocol: TCP
+ - containerPort: 1936
+ hostPort: 1936
+ name: stats
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ host: localhost
+ path: /healthz
+ port: 1937
+ scheme: HTTP
+ timeoutSeconds: 1
+ resources: {}
+ terminationMessagePath: /dev/termination-log
+ dnsPolicy: ClusterFirst
+ hostNetwork: true
+ nodeSelector:
+ type: infra
+ restartPolicy: Always
+ securityContext: {}
+ serviceAccount: router
+ serviceAccountName: router
+ terminationGracePeriodSeconds: 30
+ triggers:
+ - type: ConfigChange
+ status:
+ details:
+ causes:
+ - type: ConfigChange
+ latestVersion: 1
+
diff --git a/roles/lib_openshift_api/build/test/files/dc.yml b/roles/lib_openshift_api/build/test/files/dc.yml
new file mode 100644
index 000000000..7992c90dd
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/dc.yml
@@ -0,0 +1,121 @@
+apiVersion: v1
+kind: DeploymentConfig
+metadata:
+ creationTimestamp: 2016-03-18T19:47:45Z
+ labels:
+ router: router
+ name: router
+ namespace: default
+ resourceVersion: "84016"
+ selfLink: /oapi/v1/namespaces/default/deploymentconfigs/router
+ uid: 48f8b9d9-ed42-11e5-9903-0a9a9d4e7f2b
+spec:
+ replicas: 2
+ selector:
+ router: router
+ strategy:
+ resources: {}
+ rollingParams:
+ intervalSeconds: 1
+ maxSurge: 0
+ maxUnavailable: 25%
+ timeoutSeconds: 600
+ updatePercent: -25
+ updatePeriodSeconds: 1
+ type: Rolling
+ template:
+ metadata:
+ creationTimestamp: null
+ labels:
+ router: router
+ spec:
+ containers:
+ - env:
+ - name: DEFAULT_CERTIFICATE
+ - name: OPENSHIFT_CA_DATA
+ value: |
+ -----BEGIN CERTIFICATE-----
+ MIIC5jCCAdCgAwIBAgIBATALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo
+ -----END CERTIFICATE-----
+ - name: OPENSHIFT_CERT_DATA
+ value: |
+ -----BEGIN CERTIFICATE-----
+ MIIDDTCCAfegAwIBAgIBCDALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo
+ -----END CERTIFICATE-----
+ - name: OPENSHIFT_INSECURE
+ value: "false"
+ - name: OPENSHIFT_KEY_DATA
+ value: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEA2lf49DrPHfCdCORcnIbmDVrx8yos7trjWdBvuledijyslRVR
+ -----END RSA PRIVATE KEY-----
+ - name: OPENSHIFT_MASTER
+ value: https://internal.api.mwoodson.openshift.com
+ - 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_EXTERNAL_HOST_PARTITION_PATH
+ - name: ROUTER_EXTERNAL_HOST_PASSWORD
+ - name: ROUTER_EXTERNAL_HOST_PRIVKEY
+ value: /etc/secret-volume/router.pem
+ - name: ROUTER_EXTERNAL_HOST_USERNAME
+ - name: ROUTER_SERVICE_NAME
+ value: router
+ - name: ROUTER_SERVICE_NAMESPACE
+ value: default
+ - name: STATS_PASSWORD
+ value: ugCk6YBm4q
+ - name: STATS_PORT
+ value: "1936"
+ - name: STATS_USERNAME
+ value: admin
+ image: openshift3/ose-haproxy-router:v3.1.1.6
+ imagePullPolicy: IfNotPresent
+ livenessProbe:
+ httpGet:
+ host: localhost
+ path: /healthz
+ port: 1936
+ scheme: HTTP
+ initialDelaySeconds: 10
+ timeoutSeconds: 1
+ name: router
+ ports:
+ - containerPort: 80
+ hostPort: 80
+ protocol: TCP
+ - containerPort: 443
+ hostPort: 443
+ protocol: TCP
+ - containerPort: 1936
+ hostPort: 1936
+ name: stats
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ host: localhost
+ path: /healthz
+ port: 1936
+ scheme: HTTP
+ timeoutSeconds: 1
+ resources: {}
+ terminationMessagePath: /dev/termination-log
+ dnsPolicy: ClusterFirst
+ hostNetwork: true
+ nodeSelector:
+ type: infra
+ restartPolicy: Always
+ securityContext: {}
+ serviceAccount: router
+ serviceAccountName: router
+ terminationGracePeriodSeconds: 30
+ triggers:
+ - type: ConfigChange
+status:
+ details:
+ causes:
+ - type: ConfigChange
+ latestVersion: 1
+
diff --git a/roles/lib_openshift_api/build/test/files/passwords.yml b/roles/lib_openshift_api/build/test/files/passwords.yml
new file mode 100644
index 000000000..fadbf1d85
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/passwords.yml
@@ -0,0 +1,4 @@
+test1
+test2
+test3
+test4
diff --git a/roles/lib_openshift_api/build/test/files/router-mod.json b/roles/lib_openshift_api/build/test/files/router-mod.json
new file mode 100644
index 000000000..45e2e7c8d
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/router-mod.json
@@ -0,0 +1,30 @@
+{
+ "kind": "Service",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "router",
+ "namespace": "default",
+ "labels": {
+ "router": "router"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "81-tcp",
+ "protocol": "TCP",
+ "port": 81,
+ "targetPort": 81
+ }
+ ],
+ "selector": {
+ "router": "router"
+ },
+ "type": "ClusterIP",
+ "sessionAffinity": "None"
+ },
+ "status": {
+ "loadBalancer": {}
+ }
+}
+
diff --git a/roles/lib_openshift_api/build/test/files/router.json b/roles/lib_openshift_api/build/test/files/router.json
new file mode 100644
index 000000000..cad3c6f53
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/files/router.json
@@ -0,0 +1,29 @@
+{
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": {
+ "labels": {
+ "router": "router"
+ },
+ "name": "router",
+ "namespace": "default"
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "80-tcp",
+ "port": 80,
+ "protocol": "TCP",
+ "targetPort": 80
+ }
+ ],
+ "selector": {
+ "router": "router"
+ },
+ "sessionAffinity": "None",
+ "type": "ClusterIP"
+ },
+ "status": {
+ "loadBalancer": {}
+ }
+}
diff --git a/roles/lib_openshift_api/build/test/roles b/roles/lib_openshift_api/build/test/roles
new file mode 120000
index 000000000..ae82aa9bb
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/roles
@@ -0,0 +1 @@
+../../../../roles/ \ No newline at end of file
diff --git a/roles/lib_openshift_api/build/test/secrets.yml b/roles/lib_openshift_api/build/test/secrets.yml
new file mode 100755
index 000000000..dddc05c4d
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/secrets.yml
@@ -0,0 +1,81 @@
+#!/usr/bin/ansible-playbook
+---
+- hosts: "oo_clusterid_mwoodson:&oo_version_3:&oo_master_primary"
+ gather_facts: no
+ user: root
+
+ post_tasks:
+ - copy:
+ dest: "/tmp/{{ item }}"
+ src: "files/{{ item }}"
+ with_items:
+ - config.yml
+ - passwords.yml
+
+ - name: list secrets
+ oc_secret:
+ state: list
+ namespace: default
+ name: kenny
+ register: secret_out
+
+ - debug:
+ var: secret_out
+
+ - name: absent secrets
+ oc_secret:
+ state: absent
+ namespace: default
+ name: kenny
+ register: secret_out
+
+ - debug:
+ var: secret_out
+
+ - name: present secrets
+ oc_secret:
+ state: present
+ namespace: default
+ name: kenny
+ files:
+ - /tmp/config.yml
+ - /tmp/passwords.yml
+ delete_after: True
+ register: secret_out
+
+ - debug:
+ var: secret_out
+
+ - name: present secrets
+ oc_secret:
+ state: present
+ namespace: default
+ name: kenny
+ contents:
+ - path: config.yml
+ content: "value: True\n"
+ - path: passwords.yml
+ content: "test1\ntest2\ntest3\ntest4\n"
+ delete_after: True
+ register: secret_out
+
+ - debug:
+ var: secret_out
+
+ - name: present secrets update
+ oc_secret:
+ state: present
+ namespace: default
+ name: kenny
+ contents:
+ - path: config.yml
+ content: "value: True\n"
+ - path: passwords.yml
+ content: "test1\ntest2\ntest3\ntest4\ntest5\n"
+ delete_after: True
+ force: True
+ register: secret_out
+
+ - debug:
+ var: secret_out
+
diff --git a/roles/lib_openshift_api/build/test/services.yml b/roles/lib_openshift_api/build/test/services.yml
new file mode 100755
index 000000000..a32e8d012
--- /dev/null
+++ b/roles/lib_openshift_api/build/test/services.yml
@@ -0,0 +1,133 @@
+#!/usr/bin/ansible-playbook
+---
+- hosts: "oo_clusterid_mwoodson:&oo_master_primary"
+ gather_facts: no
+ user: root
+
+ roles:
+ - roles/lib_yaml_editor
+
+ tasks:
+ - copy:
+ dest: "/tmp/{{ item }}"
+ src: "files/{{ item }}"
+ with_items:
+ - router.json
+ - router-mod.json
+
+ - name: list services
+ oc_obj:
+ kind: service
+ state: list
+ namespace: default
+ name: router
+ register: service_out
+
+ - debug:
+ var: service_out.results
+
+ - name: absent service
+ oc_obj:
+ kind: service
+ state: absent
+ namespace: default
+ name: router
+ register: service_out
+
+ - debug:
+ var: service_out
+
+ - name: present service create
+ oc_obj:
+ kind: service
+ state: present
+ namespace: default
+ name: router
+ files:
+ - /tmp/router.json
+ delete_after: True
+ register: service_out
+
+ - debug:
+ var: service_out
+
+ - name: dump router
+ oc_obj:
+ kind: service
+ state: list
+ name: router
+ namespace: default
+ register: routerout
+
+ - name: write router file
+ copy:
+ dest: /tmp/router-mod.json
+ content: "{{ routerout.results[0] }}"
+
+ - command: cat /tmp/router-mod.json
+ register: catout
+
+ - debug:
+ msg: "{{ catout }}"
+
+ - command: "sed -i 's/80-tcp/81-tcp/g' /tmp/router-mod.json"
+ register: catout
+
+ - name: present service replace
+ oc_obj:
+ kind: service
+ state: present
+ namespace: default
+ name: router
+ files:
+ - /tmp/router-mod.json
+ #delete_after: True
+ register: service_out
+
+ - debug:
+ var: service_out
+
+ - name: list services
+ oc_obj:
+ kind: service
+ state: list
+ namespace: default
+ name: router
+ register: service_out
+
+ - debug:
+ var: service_out.results
+
+ - set_fact:
+ new_service: "{{ service_out.results[0] }}"
+
+ - yedit:
+ src: /tmp/routeryedit
+ content: "{{ new_service }}"
+ key: spec.ports
+ value:
+ - name: 80-tcp
+ port: 80
+ protocol: TCP
+ targetPort: 80
+
+ - yedit:
+ src: /tmp/routeryedit
+ state: list
+ register: yeditout
+
+ - debug:
+ var: yeditout
+
+ - name: present service replace
+ oc_obj:
+ kind: service
+ state: present
+ namespace: default
+ name: router
+ content: "{{ yeditout.results }}"
+ delete_after: True
+ register: service_out
+
+ - debug:
+ var: service_out
diff --git a/roles/lib_openshift_api/library/oc_deploymentconfig.py b/roles/lib_openshift_api/library/oc_obj.py
index fbdaa8e9c..2e07e5bb3 100644
--- a/roles/lib_openshift_api/library/oc_deploymentconfig.py
+++ b/roles/lib_openshift_api/library/oc_obj.py
@@ -1,7 +1,15 @@
-#!/usr/bin/env python
+#!usr/bin/env python
+# ___ ___ _ _ ___ ___ _ _____ ___ ___
+# / __| __| \| | __| _ \ /_\_ _| __| \
+# | (_ | _|| .` | _|| / / _ \| | | _|| |) |
+# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
+# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
+# | |) | (_) | | .` | (_) || | | _|| |) | | | |
+# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
'''
- OpenShiftCLI class that wraps the oc commands in a subprocess
+ OpenShiftCLI class that wraps the oc commands in a subprocess
'''
+
import atexit
import json
import os
@@ -9,6 +17,7 @@ import shutil
import subprocess
import yaml
+# pylint: disable=too-few-public-methods
class OpenShiftCLI(object):
''' Class to wrap the oc command line tools '''
def __init__(self,
@@ -20,22 +29,39 @@ class OpenShiftCLI(object):
self.verbose = verbose
self.kubeconfig = kubeconfig
- def replace(self, fname, force=False):
+ # Pylint allows only 5 arguments to be passed.
+ # pylint: disable=too-many-arguments
+ def _replace_content(self, resource, rname, content, force=False):
+ ''' replace the current object with the content '''
+ res = self._get(resource, rname)
+ if not res['results']:
+ return res
+
+ fname = '/tmp/%s' % rname
+ yed = Yedit(fname, res['results'][0])
+ for key, value in content.items():
+ yed.put(key, value)
+
+ atexit.register(Utils.cleanup, [fname])
+
+ return self._replace(fname, force)
+
+ def _replace(self, fname, force=False):
'''return all pods '''
- cmd = ['replace', '-f', fname]
+ cmd = ['-n', self.namespace, 'replace', '-f', fname]
if force:
- cmd = ['replace', '--force', '-f', fname]
+ cmd.append('--force')
return self.oc_cmd(cmd)
- def create(self, fname):
+ def _create(self, fname):
'''return all pods '''
return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
- def delete(self, resource, rname):
+ def _delete(self, resource, rname):
'''return all pods '''
return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
- def get(self, resource, rname=None):
+ def _get(self, resource, rname=None):
'''return a secret by name '''
cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
if rname:
@@ -96,7 +122,7 @@ class Utils(object):
path = os.path.join('/tmp', rname)
with open(path, 'w') as fds:
if ftype == 'yaml':
- fds.write(yaml.dump(data, default_flow_style=False))
+ fds.write(yaml.safe_dump(data, default_flow_style=False))
elif ftype == 'json':
fds.write(json.dumps(data))
@@ -173,7 +199,7 @@ class Utils(object):
''' Given a user defined definition, compare it with the results given back by our query. '''
# Currently these values are autogenerated and we do not need to check them
- skip = ['creationTimestamp', 'selfLink', 'resourceVersion', 'uid', 'namespace']
+ skip = ['metadata', 'status']
for key, value in result_def.items():
if key in skip:
@@ -222,45 +248,246 @@ class Utils(object):
return True
-class DeploymentConfig(OpenShiftCLI):
- ''' Class to wrap the oc command line tools
- '''
+class YeditException(Exception):
+ ''' Exception class for Yedit '''
+ pass
+
+class Yedit(object):
+ ''' Class to modify yaml files '''
+
+ def __init__(self, filename=None, content=None):
+ self.content = content
+ self.filename = filename
+ self.__yaml_dict = content
+ if self.filename and not self.content:
+ self.get()
+ elif self.filename and self.content:
+ self.write()
+
+ @property
+ def yaml_dict(self):
+ ''' getter method for yaml_dict '''
+ return self.__yaml_dict
+
+ @yaml_dict.setter
+ def yaml_dict(self, value):
+ ''' setter method for yaml_dict '''
+ self.__yaml_dict = value
+
+ @staticmethod
+ def remove_entry(data, keys):
+ ''' remove an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key in data.keys():
+ Yedit.remove_entry(data[key], rest)
+ else:
+ del data[keys]
+
+ @staticmethod
+ def add_entry(data, keys, item):
+ ''' Add an item to a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key not in data:
+ data[key] = {}
+
+ if not isinstance(data, dict):
+ raise YeditException('Invalid add_entry called on a [%s] of type [%s].' % (data, type(data)))
+ else:
+ Yedit.add_entry(data[key], rest, item)
+
+ else:
+ data[keys] = item
+
+
+ @staticmethod
+ def get_entry(data, keys):
+ ''' Get an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ return c
+ '''
+ if keys and "." in keys:
+ key, rest = keys.split(".", 1)
+ if not isinstance(data[key], dict):
+ raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
+
+ else:
+ return Yedit.get_entry(data[key], rest)
+
+ else:
+ return data.get(keys, None)
+
+
+ def write(self):
+ ''' write to file '''
+ if not self.filename:
+ raise YeditException('Please specify a filename.')
+
+ with open(self.filename, 'w') as yfd:
+ yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
+
+ def read(self):
+ ''' write to file '''
+ # check if it exists
+ if not self.exists():
+ return None
+
+ contents = None
+ with open(self.filename) as yfd:
+ contents = yfd.read()
+
+ return contents
+
+ def exists(self):
+ ''' return whether file exists '''
+ if os.path.exists(self.filename):
+ return True
+
+ return False
+
+ def get(self):
+ ''' return yaml file '''
+ contents = self.read()
+
+ if not contents:
+ return None
+
+ # check if it is yaml
+ try:
+ self.yaml_dict = yaml.load(contents)
+ except yaml.YAMLError as _:
+ # Error loading yaml
+ return None
+
+ return self.yaml_dict
+
+ def delete(self, key):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ self.write()
+ return (True, self.get())
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ self.write()
+ return (True, self.get())
+
+ def create(self, key, value):
+ ''' create the file '''
+ if not self.exists():
+ self.yaml_dict = {key: value}
+ self.write()
+ return (True, self.get())
+
+ return (False, self.get())
+
+class OCObject(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools '''
+
+ # pylint allows 5. we need 6
+ # pylint: disable=too-many-arguments
def __init__(self,
+ kind,
namespace,
- dname=None,
+ rname=None,
kubeconfig='/etc/origin/master/admin.kubeconfig',
verbose=False):
''' Constructor for OpenshiftOC '''
- super(DeploymentConfig, self).__init__(namespace, kubeconfig)
+ super(OCObject, self).__init__(namespace, kubeconfig)
+ self.kind = kind
self.namespace = namespace
- self.name = dname
+ self.name = rname
self.kubeconfig = kubeconfig
self.verbose = verbose
- def get_dc(self):
+ def get(self):
'''return a deploymentconfig by name '''
- return self.get('dc', self.name)
+ return self._get(self.kind, rname=self.name)
- def delete_dc(self):
+ def delete(self):
'''return all pods '''
- return self.delete('dc', self.name)
+ return self._delete(self.kind, self.name)
- def new_dc(self, dfile):
+ def create(self, files=None, content=None):
'''Create a deploymentconfig '''
- return self.create(dfile)
+ if files:
+ return self._create(files[0])
+
+ return self._create(Utils.create_files_from_contents(content))
- def update_dc(self, dfile, force=False):
+
+ # pylint: disable=too-many-function-args
+ def update(self, files=None, content=None, force=False):
'''run update dc
This receives a list of file names and takes the first filename and calls replace.
'''
- return self.replace(dfile, force)
+ if files:
+ return self._replace(files[0], force)
+
+ return self.update_content(content, force)
+
+ def update_content(self, content, force=False):
+ '''update the dc with the content'''
+ return self._replace_content(self.kind, self.name, content, force=force)
+
+ def needs_update(self, files=None, content=None, content_type='yaml'):
+ ''' check to see if we need to update '''
+ objects = self.get()
+ if objects['returncode'] != 0:
+ return objects
+
+ # pylint: disable=no-member
+ data = None
+ if files:
+ data = Utils.get_resource_file(files[0], content_type)
+
+ # if equal then no need. So not equal is True
+ return not Utils.check_def_equal(data, objects['results'][0], True)
+ else:
+ data = content
+
+ for key, value in data.items():
+ if key == 'metadata':
+ continue
+ if not objects['results'][0].has_key(key):
+ return True
+ if value != objects['results'][0][key]:
+ return True
+
+ return False
# pylint: disable=too-many-branches
def main():
'''
- ansible oc module for deploymentconfig
+ ansible oc module for services
'''
module = AnsibleModule(
@@ -271,24 +498,30 @@ def main():
debug=dict(default=False, type='bool'),
namespace=dict(default='default', type='str'),
name=dict(default=None, type='str'),
- deploymentconfig_file=dict(default=None, type='str'),
- input_type=dict(default='yaml', choices=['yaml', 'json'], type='str'),
+ files=dict(default=None, type='list'),
+ kind=dict(required=True,
+ type='str',
+ choices=['dc', 'deploymentconfig',
+ 'svc', 'service',
+ 'secret',
+ ]),
delete_after=dict(default=False, type='bool'),
content=dict(default=None, type='dict'),
force=dict(default=False, type='bool'),
),
- mutually_exclusive=[["contents", "deploymentconfig_file"]],
+ mutually_exclusive=[["content", "files"]],
supports_check_mode=True,
)
- occmd = DeploymentConfig(module.params['namespace'],
- dname=module.params['name'],
- kubeconfig=module.params['kubeconfig'],
- verbose=module.params['debug'])
+ ocobj = OCObject(module.params['kind'],
+ module.params['namespace'],
+ module.params['name'],
+ kubeconfig=module.params['kubeconfig'],
+ verbose=module.params['debug'])
state = module.params['state']
- api_rval = occmd.get_dc()
+ api_rval = ocobj.get()
#####
# Get
@@ -308,18 +541,10 @@ def main():
if module.check_mode:
module.exit_json(change=False, msg='Would have performed a delete.')
- api_rval = occmd.delete_dc()
+ api_rval = ocobj.delete()
module.exit_json(changed=True, results=api_rval, state="absent")
-
if state == 'present':
- if module.params['deploymentconfig_file']:
- dfile = module.params['deploymentconfig_file']
- elif module.params['content']:
- dfile = Utils.create_file('dc', module.params['content'])
- else:
- module.fail_json(msg="Please specify content or deploymentconfig file.")
-
########
# Create
########
@@ -328,40 +553,54 @@ def main():
if module.check_mode:
module.exit_json(change=False, msg='Would have performed a create.')
- api_rval = occmd.new_dc(dfile)
+ # Create it here
+ api_rval = ocobj.create(module.params['files'], module.params['content'])
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
- # Remove files
- if module.params['deploymentconfig_file'] and module.params['delete_after']:
- Utils.cleanup([dfile])
+ # return the created object
+ api_rval = ocobj.get()
if api_rval['returncode'] != 0:
module.fail_json(msg=api_rval)
+ # Remove files
+ if module.params['files'] and module.params['delete_after']:
+ Utils.cleanup(module.params['files'])
+
module.exit_json(changed=True, results=api_rval, state="present")
########
# Update
########
- if Utils.check_def_equal(Utils.get_resource_file(dfile), api_rval['results'][0]):
+ # if a file path is passed, use it.
+ update = ocobj.needs_update(module.params['files'], module.params['content'])
+ if not isinstance(update, bool):
+ module.fail_json(msg=update)
- # Remove files
- if module.params['deploymentconfig_file'] and module.params['delete_after']:
- Utils.cleanup([dfile])
+ # No changes
+ if not update:
+ if module.params['files'] and module.params['delete_after']:
+ Utils.cleanup(module.params['files'])
- module.exit_json(changed=False, results=api_rval['results'], state="present")
+ module.exit_json(changed=False, results=api_rval['results'][0], state="present")
if module.check_mode:
module.exit_json(change=False, msg='Would have performed an update.')
- api_rval = occmd.update_dc(dfile, force=module.params['force'])
+ api_rval = ocobj.update(module.params['files'],
+ module.params['content'],
+ module.params['force'])
- # Remove files
- if module.params['deploymentconfig_file'] and module.params['delete_after']:
- Utils.cleanup([dfile])
if api_rval['returncode'] != 0:
module.fail_json(msg=api_rval)
+ # return the created object
+ api_rval = ocobj.get()
+
+ if api_rval['returncode'] != 0:
+ module.fail_json(msg=api_rval)
module.exit_json(changed=True, results=api_rval, state="present")
diff --git a/roles/lib_openshift_api/library/oc_secret.py b/roles/lib_openshift_api/library/oc_secret.py
index 96a0f1db1..985ff8bfa 100644
--- a/roles/lib_openshift_api/library/oc_secret.py
+++ b/roles/lib_openshift_api/library/oc_secret.py
@@ -1,7 +1,15 @@
-#!/usr/bin/env python
+#!usr/bin/env python
+# ___ ___ _ _ ___ ___ _ _____ ___ ___
+# / __| __| \| | __| _ \ /_\_ _| __| \
+# | (_ | _|| .` | _|| / / _ \| | | _|| |) |
+# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
+# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
+# | |) | (_) | | .` | (_) || | | _|| |) | | | |
+# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
'''
- OpenShiftCLI class that wraps the oc commands in a subprocess
+ OpenShiftCLI class that wraps the oc commands in a subprocess
'''
+
import atexit
import json
import os
@@ -9,6 +17,7 @@ import shutil
import subprocess
import yaml
+# pylint: disable=too-few-public-methods
class OpenShiftCLI(object):
''' Class to wrap the oc command line tools '''
def __init__(self,
@@ -20,22 +29,39 @@ class OpenShiftCLI(object):
self.verbose = verbose
self.kubeconfig = kubeconfig
- def replace(self, fname, force=False):
+ # Pylint allows only 5 arguments to be passed.
+ # pylint: disable=too-many-arguments
+ def _replace_content(self, resource, rname, content, force=False):
+ ''' replace the current object with the content '''
+ res = self._get(resource, rname)
+ if not res['results']:
+ return res
+
+ fname = '/tmp/%s' % rname
+ yed = Yedit(fname, res['results'][0])
+ for key, value in content.items():
+ yed.put(key, value)
+
+ atexit.register(Utils.cleanup, [fname])
+
+ return self._replace(fname, force)
+
+ def _replace(self, fname, force=False):
'''return all pods '''
- cmd = ['replace', '-f', fname]
+ cmd = ['-n', self.namespace, 'replace', '-f', fname]
if force:
- cmd = ['replace', '--force', '-f', fname]
+ cmd.append('--force')
return self.oc_cmd(cmd)
- def create(self, fname):
+ def _create(self, fname):
'''return all pods '''
return self.oc_cmd(['create', '-f', fname, '-n', self.namespace])
- def delete(self, resource, rname):
+ def _delete(self, resource, rname):
'''return all pods '''
return self.oc_cmd(['delete', resource, rname, '-n', self.namespace])
- def get(self, resource, rname=None):
+ def _get(self, resource, rname=None):
'''return a secret by name '''
cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
if rname:
@@ -96,7 +122,7 @@ class Utils(object):
path = os.path.join('/tmp', rname)
with open(path, 'w') as fds:
if ftype == 'yaml':
- fds.write(yaml.dump(data, default_flow_style=False))
+ fds.write(yaml.safe_dump(data, default_flow_style=False))
elif ftype == 'json':
fds.write(json.dumps(data))
@@ -173,7 +199,7 @@ class Utils(object):
''' Given a user defined definition, compare it with the results given back by our query. '''
# Currently these values are autogenerated and we do not need to check them
- skip = ['creationTimestamp', 'selfLink', 'resourceVersion', 'uid', 'namespace']
+ skip = ['metadata', 'status']
for key, value in result_def.items():
if key in skip:
@@ -222,6 +248,165 @@ class Utils(object):
return True
+class YeditException(Exception):
+ ''' Exception class for Yedit '''
+ pass
+
+class Yedit(object):
+ ''' Class to modify yaml files '''
+
+ def __init__(self, filename=None, content=None):
+ self.content = content
+ self.filename = filename
+ self.__yaml_dict = content
+ if self.filename and not self.content:
+ self.get()
+ elif self.filename and self.content:
+ self.write()
+
+ @property
+ def yaml_dict(self):
+ ''' getter method for yaml_dict '''
+ return self.__yaml_dict
+
+ @yaml_dict.setter
+ def yaml_dict(self, value):
+ ''' setter method for yaml_dict '''
+ self.__yaml_dict = value
+
+ @staticmethod
+ def remove_entry(data, keys):
+ ''' remove an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key in data.keys():
+ Yedit.remove_entry(data[key], rest)
+ else:
+ del data[keys]
+
+ @staticmethod
+ def add_entry(data, keys, item):
+ ''' Add an item to a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key not in data:
+ data[key] = {}
+
+ if not isinstance(data, dict):
+ raise YeditException('Invalid add_entry called on a [%s] of type [%s].' % (data, type(data)))
+ else:
+ Yedit.add_entry(data[key], rest, item)
+
+ else:
+ data[keys] = item
+
+
+ @staticmethod
+ def get_entry(data, keys):
+ ''' Get an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ return c
+ '''
+ if keys and "." in keys:
+ key, rest = keys.split(".", 1)
+ if not isinstance(data[key], dict):
+ raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
+
+ else:
+ return Yedit.get_entry(data[key], rest)
+
+ else:
+ return data.get(keys, None)
+
+
+ def write(self):
+ ''' write to file '''
+ if not self.filename:
+ raise YeditException('Please specify a filename.')
+
+ with open(self.filename, 'w') as yfd:
+ yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
+
+ def read(self):
+ ''' write to file '''
+ # check if it exists
+ if not self.exists():
+ return None
+
+ contents = None
+ with open(self.filename) as yfd:
+ contents = yfd.read()
+
+ return contents
+
+ def exists(self):
+ ''' return whether file exists '''
+ if os.path.exists(self.filename):
+ return True
+
+ return False
+
+ def get(self):
+ ''' return yaml file '''
+ contents = self.read()
+
+ if not contents:
+ return None
+
+ # check if it is yaml
+ try:
+ self.yaml_dict = yaml.load(contents)
+ except yaml.YAMLError as _:
+ # Error loading yaml
+ return None
+
+ return self.yaml_dict
+
+ def delete(self, key):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ self.write()
+ return (True, self.get())
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ self.write()
+ return (True, self.get())
+
+ def create(self, key, value):
+ ''' create the file '''
+ if not self.exists():
+ self.yaml_dict = {key: value}
+ self.write()
+ return (True, self.get())
+
+ return (False, self.get())
+
class Secret(OpenShiftCLI):
''' Class to wrap the oc command line tools
'''
@@ -237,16 +422,16 @@ class Secret(OpenShiftCLI):
self.kubeconfig = kubeconfig
self.verbose = verbose
- def get_secrets(self):
+ def get(self):
'''return a secret by name '''
- return self.get('secrets', self.name)
+ return self._get('secrets', self.name)
- def delete_secret(self):
- '''return all pods '''
- return self.delete('secrets', self.name)
+ def delete(self):
+ '''delete a secret by name'''
+ return self._delete('secrets', self.name)
- def secret_new(self, files=None, contents=None):
- '''Create a secret with all pods '''
+ def create(self, files=None, contents=None):
+ '''Create a secret '''
if not files:
files = Utils.create_files_from_contents(contents)
@@ -256,7 +441,7 @@ class Secret(OpenShiftCLI):
return self.oc_cmd(cmd)
- def update_secret(self, files, force=False):
+ def update(self, files, force=False):
'''run update secret
This receives a list of file names and converts it into a secret.
@@ -272,7 +457,7 @@ class Secret(OpenShiftCLI):
atexit.register(Utils.cleanup, [sfile_path])
- return self.replace(sfile_path, force=force)
+ return self._replace(sfile_path, force=force)
def prep_secret(self, files=None, contents=None):
''' return what the secret would look like if created
@@ -319,7 +504,7 @@ def main():
state = module.params['state']
- api_rval = occmd.get_secrets()
+ api_rval = occmd.get()
#####
# Get
@@ -339,7 +524,7 @@ def main():
if module.check_mode:
module.exit_json(change=False, msg='Would have performed a delete.')
- api_rval = occmd.delete_secret()
+ api_rval = occmd.delete()
module.exit_json(changed=True, results=api_rval, state="absent")
@@ -359,7 +544,7 @@ def main():
if module.check_mode:
module.exit_json(change=False, msg='Would have performed a create.')
- api_rval = occmd.secret_new(module.params['files'], module.params['contents'])
+ api_rval = occmd.create(module.params['files'], module.params['contents'])
# Remove files
if files and module.params['delete_after']:
@@ -386,7 +571,7 @@ def main():
if module.check_mode:
module.exit_json(change=False, msg='Would have performed an update.')
- api_rval = occmd.update_secret(files, force=module.params['force'])
+ api_rval = occmd.update(files, force=module.params['force'])
# Remove files
if secret and module.params['delete_after']:
diff --git a/roles/lib_yaml_editor/build/ansible/yedit.py b/roles/lib_yaml_editor/build/ansible/yedit.py
new file mode 100644
index 000000000..bf868fb71
--- /dev/null
+++ b/roles/lib_yaml_editor/build/ansible/yedit.py
@@ -0,0 +1,66 @@
+#pylint: skip-file
+
+def main():
+ '''
+ ansible oc module for secrets
+ '''
+
+ module = AnsibleModule(
+ argument_spec=dict(
+ state=dict(default='present', type='str',
+ choices=['present', 'absent', 'list']),
+ debug=dict(default=False, type='bool'),
+ src=dict(default=None, type='str'),
+ content=dict(default=None, type='dict'),
+ key=dict(default=None, type='str'),
+ value=dict(default=None, type='str'),
+ value_format=dict(default='yaml', choices=['yaml', 'json'], type='str'),
+ ),
+ #mutually_exclusive=[["src", "content"]],
+
+ supports_check_mode=True,
+ )
+ state = module.params['state']
+
+ yamlfile = Yedit(module.params['src'], module.params['content'])
+
+ rval = yamlfile.get()
+ if not rval and state != 'present':
+ module.fail_json(msg='Error opening file [%s]. Verify that the' + \
+ ' file exists, that it is has correct permissions, and is valid yaml.')
+
+ if state == 'list':
+ module.exit_json(changed=False, results=rval, state="list")
+
+ if state == 'absent':
+ rval = yamlfile.delete(module.params['key'])
+ module.exit_json(changed=rval[0], results=rval[1], state="absent")
+
+ if state == 'present':
+
+ if module.params['value_format'] == 'yaml':
+ value = yaml.load(module.params['value'])
+ elif module.params['value_format'] == 'json':
+ value = json.loads(module.params['value'])
+
+ if rval:
+ rval = yamlfile.put(module.params['key'], value)
+ module.exit_json(changed=rval[0], results=rval[1], state="present")
+
+ if not module.params['content']:
+ rval = yamlfile.create(module.params['key'], value)
+ else:
+ yamlfile.write()
+ rval = yamlfile.get()
+ module.exit_json(changed=rval[0], results=rval[1], state="present")
+
+ module.exit_json(failed=True,
+ changed=False,
+ results='Unknown state passed. %s' % state,
+ state="unknown")
+
+# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
+# import module snippets. This are required
+from ansible.module_utils.basic import *
+
+main()
diff --git a/roles/lib_yaml_editor/build/generate.py b/roles/lib_yaml_editor/build/generate.py
new file mode 100755
index 000000000..6521ff2c1
--- /dev/null
+++ b/roles/lib_yaml_editor/build/generate.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+'''
+ Generate the openshift-ansible/roles/lib_openshift_cli/library/ modules.
+'''
+
+import os
+
+# pylint: disable=anomalous-backslash-in-string
+GEN_STR = "#!usr/bin/env python\n" + \
+ "# ___ ___ _ _ ___ ___ _ _____ ___ ___\n" + \
+ "# / __| __| \| | __| _ \ /_\_ _| __| \\\n" + \
+ "# | (_ | _|| .` | _|| / / _ \| | | _|| |) |\n" + \
+ "# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____\n" + \
+ "# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|\n" + \
+ "# | |) | (_) | | .` | (_) || | | _|| |) | | | |\n" + \
+ "# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|\n"
+
+FILES = {'yedit.py': ['src/base.py', 'src/yedit.py', 'ansible/yedit.py'],
+ }
+
+
+def main():
+ ''' combine the necessary files to create the ansible module '''
+ openshift_ansible = ('../library/')
+ for fname, parts in FILES.items():
+ with open(os.path.join(openshift_ansible, fname), 'w') as afd:
+ afd.seek(0)
+ afd.write(GEN_STR)
+ for fpart in parts:
+ with open(fpart) as pfd:
+ # first line is pylint disable so skip it
+ for idx, line in enumerate(pfd):
+ if idx == 0 and 'skip-file' in line:
+ continue
+
+ afd.write(line)
+
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/roles/lib_yaml_editor/build/src/base.py b/roles/lib_yaml_editor/build/src/base.py
new file mode 100644
index 000000000..ad8b041cf
--- /dev/null
+++ b/roles/lib_yaml_editor/build/src/base.py
@@ -0,0 +1,9 @@
+# pylint: skip-file
+
+'''
+module for managing yaml files
+'''
+
+import os
+import yaml
+
diff --git a/roles/lib_yaml_editor/build/src/yedit.py b/roles/lib_yaml_editor/build/src/yedit.py
new file mode 100644
index 000000000..4f6a91d8b
--- /dev/null
+++ b/roles/lib_yaml_editor/build/src/yedit.py
@@ -0,0 +1,160 @@
+# pylint: skip-file
+
+class YeditException(Exception):
+ ''' Exception class for Yedit '''
+ pass
+
+class Yedit(object):
+ ''' Class to modify yaml files '''
+
+ def __init__(self, filename=None, content=None):
+ self.content = content
+ self.filename = filename
+ self.__yaml_dict = content
+ if self.filename and not self.content:
+ self.get()
+ elif self.filename and self.content:
+ self.write()
+
+ @property
+ def yaml_dict(self):
+ ''' getter method for yaml_dict '''
+ return self.__yaml_dict
+
+ @yaml_dict.setter
+ def yaml_dict(self, value):
+ ''' setter method for yaml_dict '''
+ self.__yaml_dict = value
+
+ @staticmethod
+ def remove_entry(data, keys):
+ ''' remove an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key in data.keys():
+ Yedit.remove_entry(data[key], rest)
+ else:
+ del data[keys]
+
+ @staticmethod
+ def add_entry(data, keys, item):
+ ''' Add an item to a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ item = c
+ '''
+ if "." in keys:
+ key, rest = keys.split(".", 1)
+ if key not in data:
+ data[key] = {}
+
+ if not isinstance(data, dict):
+ raise YeditException('Invalid add_entry called on a [%s] of type [%s].' % (data, type(data)))
+ else:
+ Yedit.add_entry(data[key], rest, item)
+
+ else:
+ data[keys] = item
+
+
+ @staticmethod
+ def get_entry(data, keys):
+ ''' Get an item from a dictionary with key notation a.b.c
+ d = {'a': {'b': 'c'}}}
+ keys = a.b
+ return c
+ '''
+ if keys and "." in keys:
+ key, rest = keys.split(".", 1)
+ if not isinstance(data[key], dict):
+ raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
+
+ else:
+ return Yedit.get_entry(data[key], rest)
+
+ else:
+ return data.get(keys, None)
+
+
+ def write(self):
+ ''' write to file '''
+ if not self.filename:
+ raise YeditException('Please specify a filename.')
+
+ with open(self.filename, 'w') as yfd:
+ yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
+
+ def read(self):
+ ''' write to file '''
+ # check if it exists
+ if not self.exists():
+ return None
+
+ contents = None
+ with open(self.filename) as yfd:
+ contents = yfd.read()
+
+ return contents
+
+ def exists(self):
+ ''' return whether file exists '''
+ if os.path.exists(self.filename):
+ return True
+
+ return False
+
+ def get(self):
+ ''' return yaml file '''
+ contents = self.read()
+
+ if not contents:
+ return None
+
+ # check if it is yaml
+ try:
+ self.yaml_dict = yaml.load(contents)
+ except yaml.YAMLError as _:
+ # Error loading yaml
+ return None
+
+ return self.yaml_dict
+
+ def delete(self, key):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ self.write()
+ return (True, self.get())
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ self.write()
+ return (True, self.get())
+
+ def create(self, key, value):
+ ''' create the file '''
+ if not self.exists():
+ self.yaml_dict = {key: value}
+ self.write()
+ return (True, self.get())
+
+ return (False, self.get())
diff --git a/roles/lib_yaml_editor/build/test/foo.yml b/roles/lib_yaml_editor/build/test/foo.yml
new file mode 100644
index 000000000..2a7a89ce2
--- /dev/null
+++ b/roles/lib_yaml_editor/build/test/foo.yml
@@ -0,0 +1 @@
+foo: barplus
diff --git a/roles/lib_yaml_editor/build/test/test.yaml b/roles/lib_yaml_editor/build/test/test.yaml
new file mode 100755
index 000000000..ac9c37565
--- /dev/null
+++ b/roles/lib_yaml_editor/build/test/test.yaml
@@ -0,0 +1,15 @@
+#!/usr/bin/ansible-playbook
+---
+- hosts: localhost
+ gather_facts: no
+ tasks:
+ - yedit:
+ src: /home/kwoodson/git/openshift-ansible/roles/lib_yaml_editor/build/test/foo.yml
+ key: foo
+ value: barplus
+ state: present
+ register: output
+
+ - debug:
+ msg: "{{ output }}"
+
diff --git a/roles/lib_yaml_editor/library/yedit.py b/roles/lib_yaml_editor/library/yedit.py
index 9b565d0c7..356cf07a5 100644
--- a/roles/lib_yaml_editor/library/yedit.py
+++ b/roles/lib_yaml_editor/library/yedit.py
@@ -1,11 +1,20 @@
-#!/usr/bin/env python
+#!usr/bin/env python
+# ___ ___ _ _ ___ ___ _ _____ ___ ___
+# / __| __| \| | __| _ \ /_\_ _| __| \
+# | (_ | _|| .` | _|| / / _ \| | | _|| |) |
+# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
+# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
+# | |) | (_) | | .` | (_) || | | _|| |) | | | |
+# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
+
'''
-module for openshift cloud secrets
+module for managing yaml files
'''
import os
import yaml
+
class YeditException(Exception):
''' Exception class for Yedit '''
pass
@@ -13,10 +22,14 @@ class YeditException(Exception):
class Yedit(object):
''' Class to modify yaml files '''
- def __init__(self, filename):
+ def __init__(self, filename=None, content=None):
+ self.content = content
self.filename = filename
- self.__yaml_dict = None
- self.get()
+ self.__yaml_dict = content
+ if self.filename and not self.content:
+ self.get()
+ elif self.filename and self.content:
+ self.write()
@property
def yaml_dict(self):
@@ -84,8 +97,11 @@ class Yedit(object):
def write(self):
''' write to file '''
+ if not self.filename:
+ raise YeditException('Please specify a filename.')
+
with open(self.filename, 'w') as yfd:
- yfd.write(yaml.dump(self.yaml_dict, default_flow_style=False))
+ yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
def read(self):
''' write to file '''
@@ -105,6 +121,7 @@ class Yedit(object):
return True
return False
+
def get(self):
''' return yaml file '''
contents = self.read()
@@ -157,7 +174,6 @@ class Yedit(object):
return (False, self.get())
-
def main():
'''
ansible oc module for secrets
@@ -168,19 +184,19 @@ def main():
state=dict(default='present', type='str',
choices=['present', 'absent', 'list']),
debug=dict(default=False, type='bool'),
-
src=dict(default=None, type='str'),
+ content=dict(default=None, type='dict'),
key=dict(default=None, type='str'),
value=dict(default=None, type='str'),
value_format=dict(default='yaml', choices=['yaml', 'json'], type='str'),
),
- mutually_exclusive=[["contents", "files"]],
+ #mutually_exclusive=[["src", "content"]],
supports_check_mode=True,
)
state = module.params['state']
- yamlfile = Yedit(module.params['src'])
+ yamlfile = Yedit(module.params['src'], module.params['content'])
rval = yamlfile.get()
if not rval and state != 'present':
@@ -205,7 +221,11 @@ def main():
rval = yamlfile.put(module.params['key'], value)
module.exit_json(changed=rval[0], results=rval[1], state="present")
- rval = yamlfile.create(module.params['key'], value)
+ if not module.params['content']:
+ rval = yamlfile.create(module.params['key'], value)
+ else:
+ yamlfile.write()
+ rval = yamlfile.get()
module.exit_json(changed=rval[0], results=rval[1], state="present")
module.exit_json(failed=True,
diff --git a/test/env-setup b/test/env-setup
index 156593571..b05df0f9e 100644
--- a/test/env-setup
+++ b/test/env-setup
@@ -2,7 +2,7 @@
CUR_PATH=$(pwd)
-PREFIX_PYTHONPATH=$CUR_PATH/inventory/
+PREFIX_PYTHONPATH=$CUR_PATH/inventory/:$CUR_PATH/roles/lib_yaml_editor/build/src
export PYTHONPATH=$PREFIX_PYTHONPATH:$PYTHONPATH
diff --git a/test/units/yedit_test.py b/test/units/yedit_test.py
index cdd2d2b59..e701cfa7c 100755
--- a/test/units/yedit_test.py
+++ b/test/units/yedit_test.py
@@ -5,164 +5,12 @@
import unittest
import os
-import yaml
-
-class YeditException(Exception):
- ''' Exception class for Yedit '''
- pass
-
-class Yedit(object):
- ''' Class to modify yaml files '''
-
- def __init__(self, filename):
- self.filename = filename
- self.__yaml_dict = None
- self.get()
-
- @property
- def yaml_dict(self):
- ''' get property for yaml_dict '''
- return self.__yaml_dict
-
- @yaml_dict.setter
- def yaml_dict(self, value):
- ''' setter method for yaml_dict '''
- self.__yaml_dict = value
-
- @staticmethod
- def remove_entry(data, keys):
- ''' remove an item from a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
- '''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key in data.keys():
- Yedit.remove_entry(data[key], rest)
- else:
- del data[keys]
-
- @staticmethod
- def add_entry(data, keys, item):
- ''' Add an item to a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
- '''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key not in data:
- data[key] = {}
-
- if not isinstance(data, dict):
- raise YeditException('Invalid add_entry called on data [%s].' % data)
- else:
- Yedit.add_entry(data[key], rest, item)
-
- else:
- data[keys] = item
-
-
- @staticmethod
- def get_entry(data, keys):
- ''' Get an item from a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- return c
- '''
- if keys and "." in keys:
- key, rest = keys.split(".", 1)
- if not isinstance(data[key], dict):
- raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
-
- else:
- return Yedit.get_entry(data[key], rest)
-
- else:
- return data.get(keys, None)
-
-
- def write(self):
- ''' write to file '''
- with open(self.filename, 'w') as yfd:
- yfd.write(yaml.dump(self.yaml_dict, default_flow_style=False))
-
- def read(self):
- ''' write to file '''
- # check if it exists
- if not self.exists():
- return None
-
- contents = None
- with open(self.filename) as yfd:
- contents = yfd.read()
-
- return contents
-
- def exists(self):
- ''' return whether file exists '''
- if os.path.exists(self.filename):
- return True
-
- return False
- def get(self):
- ''' return yaml file '''
- contents = self.read()
-
- if not contents:
- return None
-
- # check if it is yaml
- try:
- self.yaml_dict = yaml.load(contents)
- except yaml.YAMLError as _:
- # Error loading yaml
- return None
-
- return self.yaml_dict
-
- def delete(self, key):
- ''' put key, value into a yaml file '''
- try:
- entry = Yedit.get_entry(self.yaml_dict, key)
- except KeyError as _:
- entry = None
- if not entry:
- return (False, self.yaml_dict)
-
- Yedit.remove_entry(self.yaml_dict, key)
- self.write()
- return (True, self.get())
-
- def put(self, key, value):
- ''' put key, value into a yaml file '''
- try:
- entry = Yedit.get_entry(self.yaml_dict, key)
- except KeyError as _:
- entry = None
-
- if entry == value:
- return (False, self.yaml_dict)
-
- Yedit.add_entry(self.yaml_dict, key, value)
- self.write()
- return (True, self.get())
-
- def create(self, key, value):
- ''' create the file '''
- if not self.exists():
- self.yaml_dict = {key: value}
- self.write()
- return (True, self.get())
-
- return (False, self.get())
-
-
# Removing invalid variable names for tests so that I can
# keep them brief
-# pylint: disable=invalid-name
+# pylint: disable=invalid-name,no-name-in-module
+from yedit import Yedit
+
class YeditTest(unittest.TestCase):
'''
Test class for yedit
@@ -226,6 +74,15 @@ class YeditTest(unittest.TestCase):
yed.write()
yed.get()
self.assertTrue(yed.yaml_dict.has_key('foo'))
+ self.assertTrue(yed.yaml_dict['foo'] == 'bar')
+
+ def test_create_content(self):
+ '''Testing a create with content '''
+ content = {"foo": "bar"}
+ yed = Yedit("yedit_test.yml", content)
+ yed.write()
+ yed.get()
+ self.assertTrue(yed.yaml_dict.has_key('foo'))
self.assertTrue(yed.yaml_dict['foo'], 'bar')
def tearDown(self):