summaryrefslogtreecommitdiffstats
path: root/roles/lib_openshift/src
diff options
context:
space:
mode:
Diffstat (limited to 'roles/lib_openshift/src')
-rw-r--r--roles/lib_openshift/src/ansible/oc_adm_csr.py36
-rw-r--r--roles/lib_openshift/src/ansible/oc_adm_registry.py2
-rw-r--r--roles/lib_openshift/src/ansible/oc_adm_router.py2
-rw-r--r--roles/lib_openshift/src/ansible/oc_atomic_container.py25
-rw-r--r--roles/lib_openshift/src/ansible/oc_pvc.py6
-rw-r--r--roles/lib_openshift/src/ansible/oc_route.py1
-rw-r--r--roles/lib_openshift/src/ansible/oc_secret.py2
-rw-r--r--roles/lib_openshift/src/ansible/oc_service.py1
-rw-r--r--roles/lib_openshift/src/ansible/oc_storageclass.py32
-rw-r--r--roles/lib_openshift/src/class/oc_adm_csr.py209
-rw-r--r--roles/lib_openshift/src/class/oc_adm_policy_group.py41
-rw-r--r--roles/lib_openshift/src/class/oc_adm_policy_user.py47
-rw-r--r--roles/lib_openshift/src/class/oc_adm_registry.py2
-rw-r--r--roles/lib_openshift/src/class/oc_adm_router.py2
-rw-r--r--roles/lib_openshift/src/class/oc_clusterrole.py2
-rw-r--r--roles/lib_openshift/src/class/oc_obj.py21
-rw-r--r--roles/lib_openshift/src/class/oc_process.py2
-rw-r--r--roles/lib_openshift/src/class/oc_project.py3
-rw-r--r--roles/lib_openshift/src/class/oc_pvc.py2
-rw-r--r--roles/lib_openshift/src/class/oc_route.py8
-rw-r--r--roles/lib_openshift/src/class/oc_secret.py24
-rw-r--r--roles/lib_openshift/src/class/oc_service.py5
-rw-r--r--roles/lib_openshift/src/class/oc_storageclass.py155
-rw-r--r--roles/lib_openshift/src/doc/csr80
-rw-r--r--roles/lib_openshift/src/doc/obj4
-rw-r--r--roles/lib_openshift/src/doc/pvc12
-rw-r--r--roles/lib_openshift/src/doc/route6
-rw-r--r--roles/lib_openshift/src/doc/secret12
-rw-r--r--roles/lib_openshift/src/doc/service7
-rw-r--r--roles/lib_openshift/src/doc/storageclass86
-rw-r--r--roles/lib_openshift/src/lib/base.py82
-rw-r--r--roles/lib_openshift/src/lib/pvc.py54
-rw-r--r--roles/lib_openshift/src/lib/route.py4
-rw-r--r--roles/lib_openshift/src/lib/secret.py9
-rw-r--r--roles/lib_openshift/src/lib/service.py61
-rw-r--r--roles/lib_openshift/src/lib/storageclass.py73
-rw-r--r--roles/lib_openshift/src/sources.yml21
-rw-r--r--roles/lib_openshift/src/test/integration/filter_plugins/test_filters.py (renamed from roles/lib_openshift/src/test/integration/filter_plugins/filters.py)0
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_adm_csr.yml28
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_configmap.yml4
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_pvc.yml28
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_service.yml5
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_storageclass.yml87
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_adm_registry.py3
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_adm_router.py3
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_configmap.py6
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_pvc.py11
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_route.py11
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_secret.py4
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_service.py182
-rwxr-xr-xroles/lib_openshift/src/test/unit/test_oc_storageclass.py93
51 files changed, 1469 insertions, 137 deletions
diff --git a/roles/lib_openshift/src/ansible/oc_adm_csr.py b/roles/lib_openshift/src/ansible/oc_adm_csr.py
new file mode 100644
index 000000000..9e43a810b
--- /dev/null
+++ b/roles/lib_openshift/src/ansible/oc_adm_csr.py
@@ -0,0 +1,36 @@
+# pylint: skip-file
+# flake8: noqa
+
+def main():
+ '''
+ ansible oc module for approving certificate signing requests
+ '''
+
+ module = AnsibleModule(
+ argument_spec=dict(
+ kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
+ state=dict(default='approve', type='str',
+ choices=['approve', 'deny', 'list']),
+ debug=dict(default=False, type='bool'),
+ nodes=dict(default=None, type='list'),
+ timeout=dict(default=30, type='int'),
+ approve_all=dict(default=False, type='bool'),
+ service_account=dict(default='node-bootstrapper', type='str'),
+ ),
+ supports_check_mode=True,
+ mutually_exclusive=[['approve_all', 'nodes']],
+ )
+
+ if module.params['nodes'] == []:
+ module.fail_json(**dict(failed=True, msg='Please specify hosts.'))
+
+ rval = OCcsr.run_ansible(module.params, module.check_mode)
+
+ if 'failed' in rval:
+ return module.fail_json(**rval)
+
+ return module.exit_json(**rval)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/roles/lib_openshift/src/ansible/oc_adm_registry.py b/roles/lib_openshift/src/ansible/oc_adm_registry.py
index c85973c7d..d669a3488 100644
--- a/roles/lib_openshift/src/ansible/oc_adm_registry.py
+++ b/roles/lib_openshift/src/ansible/oc_adm_registry.py
@@ -17,7 +17,7 @@ def main():
kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
images=dict(default=None, type='str'),
latest_images=dict(default=False, type='bool'),
- labels=dict(default=None, type='list'),
+ labels=dict(default=None, type='dict'),
ports=dict(default=['5000'], type='list'),
replicas=dict(default=1, type='int'),
selector=dict(default=None, type='str'),
diff --git a/roles/lib_openshift/src/ansible/oc_adm_router.py b/roles/lib_openshift/src/ansible/oc_adm_router.py
index b6f8e90d0..c6563cc2f 100644
--- a/roles/lib_openshift/src/ansible/oc_adm_router.py
+++ b/roles/lib_openshift/src/ansible/oc_adm_router.py
@@ -21,7 +21,7 @@ def main():
key_file=dict(default=None, type='str'),
images=dict(default=None, type='str'), #'openshift3/ose-${component}:${version}'
latest_images=dict(default=False, type='bool'),
- labels=dict(default=None, type='list'),
+ labels=dict(default=None, type='dict'),
ports=dict(default=['80:80', '443:443'], type='list'),
replicas=dict(default=1, type='int'),
selector=dict(default=None, type='str'),
diff --git a/roles/lib_openshift/src/ansible/oc_atomic_container.py b/roles/lib_openshift/src/ansible/oc_atomic_container.py
index 20d75cb63..454d7c4b2 100644
--- a/roles/lib_openshift/src/ansible/oc_atomic_container.py
+++ b/roles/lib_openshift/src/ansible/oc_atomic_container.py
@@ -1,20 +1,25 @@
# pylint: skip-file
# flake8: noqa
-# pylint: disable=wrong-import-position,too-many-branches,invalid-name
+# pylint: disable=wrong-import-position,too-many-branches,invalid-name,no-name-in-module, import-error
import json
+
+from distutils.version import StrictVersion
+
from ansible.module_utils.basic import AnsibleModule
def _install(module, container, image, values_list):
''' install a container using atomic CLI. values_list is the list of --set arguments.
container is the name given to the container. image is the image to use for the installation. '''
- args = ['atomic', 'install', "--system", '--name=%s' % container] + values_list + [image]
+ # NOTE: system-package=no is hardcoded. This should be changed to an option in the future.
+ args = ['atomic', 'install', '--system', '--system-package=no',
+ '--name=%s' % container] + values_list + [image]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
return rc, out, err, False
else:
- changed = "Extracting" in out
+ changed = "Extracting" in out or "Copying blob" in out
return rc, out, err, changed
def _uninstall(module, name):
@@ -58,7 +63,7 @@ def do_update(module, container, old_image, image, values_list):
if rc != 0:
module.fail_json(rc=rc, msg=err)
else:
- changed = "Extracting" in out
+ changed = "Extracting" in out or "Copying blob" in out
module.exit_json(msg=out, changed=changed)
@@ -93,7 +98,9 @@ def core(module):
module.fail_json(rc=rc, msg=err)
return
- containers = json.loads(out)
+ # NOTE: "or '[]' is a workaround until atomic containers list --json
+ # provides an empty list when no containers are present.
+ containers = json.loads(out or '[]')
present = len(containers) > 0
old_image = containers[0]["image_name"] if present else None
@@ -123,9 +130,15 @@ def main():
)
# Verify that the platform supports atomic command
- rc, _, err = module.run_command('atomic -v', check_rc=False)
+ rc, version_out, err = module.run_command('rpm -q --queryformat "%{VERSION}\n" atomic', check_rc=False)
if rc != 0:
module.fail_json(msg="Error in running atomic command", err=err)
+ # This module requires atomic version 1.17.2 or later
+ atomic_version = StrictVersion(version_out.replace('\n', ''))
+ if atomic_version < StrictVersion('1.17.2'):
+ module.fail_json(
+ msg="atomic version 1.17.2+ is required",
+ err=str(atomic_version))
try:
core(module)
diff --git a/roles/lib_openshift/src/ansible/oc_pvc.py b/roles/lib_openshift/src/ansible/oc_pvc.py
index a5181e281..c98d811d6 100644
--- a/roles/lib_openshift/src/ansible/oc_pvc.py
+++ b/roles/lib_openshift/src/ansible/oc_pvc.py
@@ -16,9 +16,9 @@ def main():
name=dict(default=None, required=True, type='str'),
namespace=dict(default=None, required=True, type='str'),
volume_capacity=dict(default='1G', type='str'),
- access_modes=dict(default='ReadWriteOnce',
- choices=['ReadWriteOnce', 'ReadOnlyMany', 'ReadWriteMany'],
- type='str'),
+ storage_class_name=dict(default=None, required=False, type='str'),
+ selector=dict(default=None, required=False, type='dict'),
+ access_modes=dict(default=['ReadWriteOnce'], type='list'),
),
supports_check_mode=True,
)
diff --git a/roles/lib_openshift/src/ansible/oc_route.py b/roles/lib_openshift/src/ansible/oc_route.py
index f2f5c5095..969cf8bcd 100644
--- a/roles/lib_openshift/src/ansible/oc_route.py
+++ b/roles/lib_openshift/src/ansible/oc_route.py
@@ -13,6 +13,7 @@ def main():
state=dict(default='present', type='str',
choices=['present', 'absent', 'list']),
debug=dict(default=False, type='bool'),
+ labels=dict(default=None, type='dict'),
name=dict(default=None, required=True, type='str'),
namespace=dict(default=None, required=True, type='str'),
tls_termination=dict(default=None, type='str'),
diff --git a/roles/lib_openshift/src/ansible/oc_secret.py b/roles/lib_openshift/src/ansible/oc_secret.py
index 1337cbbe5..ee2827e69 100644
--- a/roles/lib_openshift/src/ansible/oc_secret.py
+++ b/roles/lib_openshift/src/ansible/oc_secret.py
@@ -15,6 +15,8 @@ def main():
debug=dict(default=False, type='bool'),
namespace=dict(default='default', type='str'),
name=dict(default=None, type='str'),
+ annotations=dict(default=None, type='dict'),
+ type=dict(default=None, type='str'),
files=dict(default=None, type='list'),
delete_after=dict(default=False, type='bool'),
contents=dict(default=None, type='list'),
diff --git a/roles/lib_openshift/src/ansible/oc_service.py b/roles/lib_openshift/src/ansible/oc_service.py
index 9eb144e9c..b90c08255 100644
--- a/roles/lib_openshift/src/ansible/oc_service.py
+++ b/roles/lib_openshift/src/ansible/oc_service.py
@@ -21,6 +21,7 @@ def main():
ports=dict(default=None, type='list'),
session_affinity=dict(default='None', type='str'),
service_type=dict(default='ClusterIP', type='str'),
+ external_ips=dict(default=None, type='list'),
),
supports_check_mode=True,
)
diff --git a/roles/lib_openshift/src/ansible/oc_storageclass.py b/roles/lib_openshift/src/ansible/oc_storageclass.py
new file mode 100644
index 000000000..e9f3ebbd3
--- /dev/null
+++ b/roles/lib_openshift/src/ansible/oc_storageclass.py
@@ -0,0 +1,32 @@
+# pylint: skip-file
+# flake8: noqa
+
+def main():
+ '''
+ ansible oc module for storageclass
+ '''
+
+ 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'),
+ name=dict(default=None, type='str'),
+ annotations=dict(default=None, type='dict'),
+ parameters=dict(default=None, type='dict'),
+ provisioner=dict(required=True, type='str', choices=['aws-ebs', 'gce-pd', 'glusterfs', 'cinder']),
+ api_version=dict(default='v1', type='str'),
+ default_storage_class=dict(default="false", type='str'),
+ ),
+ supports_check_mode=True,
+ )
+
+ rval = OCStorageClass.run_ansible(module.params, module.check_mode)
+ if 'failed' in rval:
+ return module.fail_json(**rval)
+
+ return module.exit_json(**rval)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/roles/lib_openshift/src/class/oc_adm_csr.py b/roles/lib_openshift/src/class/oc_adm_csr.py
new file mode 100644
index 000000000..22b8f9165
--- /dev/null
+++ b/roles/lib_openshift/src/class/oc_adm_csr.py
@@ -0,0 +1,209 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+class OCcsr(OpenShiftCLI):
+ ''' Class to wrap the oc adm certificate command line'''
+ kind = 'csr'
+
+ # pylint: disable=too-many-arguments
+ def __init__(self,
+ nodes=None,
+ approve_all=False,
+ service_account=None,
+ kubeconfig='/etc/origin/master/admin.kubeconfig',
+ verbose=False):
+ ''' Constructor for oc adm certificate '''
+ super(OCcsr, self).__init__(None, kubeconfig, verbose)
+ self.service_account = service_account
+ self.nodes = self.create_nodes(nodes)
+ self._csrs = []
+ self.approve_all = approve_all
+ self.verbose = verbose
+
+ @property
+ def csrs(self):
+ '''property for managing csrs'''
+ # any processing needed??
+ self._csrs = self._get(resource=self.kind)['results'][0]['items']
+ return self._csrs
+
+ def create_nodes(self, nodes):
+ '''create a node object to track csr signing status'''
+ nodes_list = []
+
+ if nodes is None:
+ return nodes_list
+
+ results = self._get(resource='nodes')['results'][0]['items']
+
+ for node in nodes:
+ nodes_list.append(dict(name=node, csrs={}, accepted=False, denied=False))
+
+ for ocnode in results:
+ if node in ocnode['metadata']['name']:
+ nodes_list[-1]['accepted'] = True
+
+ return nodes_list
+
+ def get(self):
+ '''get the current certificate signing requests'''
+ return self.csrs
+
+ @staticmethod
+ def action_needed(csr, action):
+ '''check to see if csr is in desired state'''
+ if csr['status'] == {}:
+ return True
+
+ state = csr['status']['conditions'][0]['type']
+
+ if action == 'approve' and state != 'Approved':
+ return True
+
+ elif action == 'deny' and state != 'Denied':
+ return True
+
+ return False
+
+ def get_csr_request(self, request):
+ '''base64 decode the request object and call openssl to determine the
+ subject and specifically the CN: from the request
+
+ Output:
+ (0, '...
+ Subject: O=system:nodes, CN=system:node:ip-172-31-54-54.ec2.internal
+ ...')
+ '''
+ import base64
+ return self._run(['openssl', 'req', '-noout', '-text'], base64.b64decode(request))[1]
+
+ def match_node(self, csr):
+ '''match an inc csr to a node in self.nodes'''
+ for node in self.nodes:
+ # we need to match based upon the csr's request certificate's CN
+ if node['name'] in self.get_csr_request(csr['spec']['request']):
+ node['csrs'][csr['metadata']['name']] = csr
+
+ # check that the username is the node and type is 'Approved'
+ if node['name'] in csr['spec']['username'] and csr['status']:
+ if csr['status']['conditions'][0]['type'] == 'Approved':
+ node['accepted'] = True
+ # check type is 'Denied' and mark node as such
+ if csr['status'] and csr['status']['conditions'][0]['type'] == 'Denied':
+ node['denied'] = True
+
+ return node
+
+ return None
+
+ def finished(self):
+ '''determine if there are more csrs to sign'''
+ # if nodes is set and we have nodes then return if all nodes are 'accepted'
+ if self.nodes is not None and len(self.nodes) > 0:
+ return all([node['accepted'] or node['denied'] for node in self.nodes])
+
+ # we are approving everything or we still have nodes outstanding
+ return False
+
+ def manage(self, action):
+ '''run openshift oc adm ca create-server-cert cmd and store results into self.nodes
+
+ we attempt to verify if the node is one that was given to us to accept.
+
+ action - (allow | deny)
+ '''
+
+ results = []
+ # There are 2 types of requests:
+ # - node-bootstrapper-client-ip-172-31-51-246-ec2-internal
+ # The client request allows the client to talk to the api/controller
+ # - node-bootstrapper-server-ip-172-31-51-246-ec2-internal
+ # The server request allows the server to join the cluster
+ # Here we need to determine how to approve/deny
+ # we should query the csrs and verify they are from the nodes we thought
+ for csr in self.csrs:
+ node = self.match_node(csr)
+ # oc adm certificate <approve|deny> csr
+ # there are 3 known states: Denied, Aprroved, {}
+ # verify something is needed by OCcsr.action_needed
+ # if approve_all, then do it
+ # if you passed in nodes, you must have a node that matches
+ if self.approve_all or (node and OCcsr.action_needed(csr, action)):
+ result = self.openshift_cmd(['certificate', action, csr['metadata']['name']], oadm=True)
+ # client should have service account name in username field
+ # server should have node name in username field
+ if node and csr['metadata']['name'] not in node['csrs']:
+ node['csrs'][csr['metadata']['name']] = csr
+
+ # accept node in cluster
+ if node['name'] in csr['spec']['username']:
+ node['accepted'] = True
+
+ results.append(result)
+
+ return results
+
+ @staticmethod
+ def run_ansible(params, check_mode=False):
+ '''run the idempotent ansible code'''
+
+ client = OCcsr(params['nodes'],
+ params['approve_all'],
+ params['service_account'],
+ params['kubeconfig'],
+ params['debug'])
+
+ state = params['state']
+
+ api_rval = client.get()
+
+ if state == 'list':
+ return {'changed': False, 'results': api_rval, 'state': state}
+
+ if state in ['approve', 'deny']:
+ if check_mode:
+ return {'changed': True,
+ 'msg': "CHECK_MODE: Would have {} the certificate.".format(params['state']),
+ 'state': state}
+
+ all_results = []
+ finished = False
+ timeout = False
+ import time
+ # loop for timeout or block until all nodes pass
+ ctr = 0
+ while True:
+
+ all_results.extend(client.manage(params['state']))
+ if client.finished():
+ finished = True
+ break
+
+ if params['timeout'] == 0:
+ if not params['approve_all']:
+ ctr = 0
+
+ if ctr * 2 > params['timeout']:
+ timeout = True
+ break
+
+ # This provides time for the nodes to send their csr requests between approvals
+ time.sleep(2)
+
+ ctr += 1
+
+ for result in all_results:
+ if result['returncode'] != 0:
+ return {'failed': True, 'msg': all_results}
+
+ return dict(changed=len(all_results) > 0,
+ results=all_results,
+ nodes=client.nodes,
+ state=state,
+ finished=finished,
+ timeout=timeout)
+
+ return {'failed': True,
+ 'msg': 'Unknown state passed. %s' % state}
+
diff --git a/roles/lib_openshift/src/class/oc_adm_policy_group.py b/roles/lib_openshift/src/class/oc_adm_policy_group.py
index 1e51913e0..6ad57bdce 100644
--- a/roles/lib_openshift/src/class/oc_adm_policy_group.py
+++ b/roles/lib_openshift/src/class/oc_adm_policy_group.py
@@ -41,28 +41,28 @@ class PolicyGroup(OpenShiftCLI):
self.verbose = verbose
self._rolebinding = None
self._scc = None
- self._cluster_policy_bindings = None
- self._policy_bindings = None
+ self._cluster_role_bindings = None
+ self._role_bindings = None
@property
- def policybindings(self):
- if self._policy_bindings is None:
- results = self._get('clusterpolicybindings', None)
+ def rolebindings(self):
+ if self._role_bindings is None:
+ results = self._get('rolebindings', None)
if results['returncode'] != 0:
- raise OpenShiftCLIError('Could not retrieve policybindings')
- self._policy_bindings = results['results'][0]['items'][0]
+ raise OpenShiftCLIError('Could not retrieve rolebindings')
+ self._role_bindings = results['results'][0]['items']
- return self._policy_bindings
+ return self._role_bindings
@property
- def clusterpolicybindings(self):
- if self._cluster_policy_bindings is None:
- results = self._get('clusterpolicybindings', None)
+ def clusterrolebindings(self):
+ if self._cluster_role_bindings is None:
+ results = self._get('clusterrolebindings', None)
if results['returncode'] != 0:
- raise OpenShiftCLIError('Could not retrieve clusterpolicybindings')
- self._cluster_policy_bindings = results['results'][0]['items'][0]
+ raise OpenShiftCLIError('Could not retrieve clusterrolebindings')
+ self._cluster_role_bindings = results['results'][0]['items']
- return self._cluster_policy_bindings
+ return self._cluster_role_bindings
@property
def role_binding(self):
@@ -105,18 +105,17 @@ class PolicyGroup(OpenShiftCLI):
''' return whether role_binding exists '''
bindings = None
if self.config.config_options['resource_kind']['value'] == 'cluster-role':
- bindings = self.clusterpolicybindings
+ bindings = self.clusterrolebindings
else:
- bindings = self.policybindings
+ bindings = self.rolebindings
if bindings is None:
return False
- for binding in bindings['roleBindings']:
- _rb = binding['roleBinding']
- if _rb['roleRef']['name'] == self.config.config_options['name']['value'] and \
- _rb['groupNames'] is not None and \
- self.config.config_options['group']['value'] in _rb['groupNames']:
+ for binding in bindings:
+ if binding['roleRef']['name'] == self.config.config_options['name']['value'] and \
+ binding['groupNames'] is not None and \
+ self.config.config_options['group']['value'] in binding['groupNames']:
self.role_binding = binding
return True
diff --git a/roles/lib_openshift/src/class/oc_adm_policy_user.py b/roles/lib_openshift/src/class/oc_adm_policy_user.py
index 37a685ebb..6fc8145c8 100644
--- a/roles/lib_openshift/src/class/oc_adm_policy_user.py
+++ b/roles/lib_openshift/src/class/oc_adm_policy_user.py
@@ -32,36 +32,36 @@ class PolicyUser(OpenShiftCLI):
''' Class to handle attaching policies to users '''
def __init__(self,
- policy_config,
+ config,
verbose=False):
''' Constructor for PolicyUser '''
- super(PolicyUser, self).__init__(policy_config.namespace, policy_config.kubeconfig, verbose)
- self.config = policy_config
+ super(PolicyUser, self).__init__(config.namespace, config.kubeconfig, verbose)
+ self.config = config
self.verbose = verbose
self._rolebinding = None
self._scc = None
- self._cluster_policy_bindings = None
- self._policy_bindings = None
+ self._cluster_role_bindings = None
+ self._role_bindings = None
@property
- def policybindings(self):
- if self._policy_bindings is None:
- results = self._get('policybindings', None)
+ def rolebindings(self):
+ if self._role_bindings is None:
+ results = self._get('rolebindings', None)
if results['returncode'] != 0:
- raise OpenShiftCLIError('Could not retrieve policybindings')
- self._policy_bindings = results['results'][0]['items'][0]
+ raise OpenShiftCLIError('Could not retrieve rolebindings')
+ self._role_bindings = results['results'][0]['items']
- return self._policy_bindings
+ return self._role_bindings
@property
- def clusterpolicybindings(self):
- if self._cluster_policy_bindings is None:
- results = self._get('clusterpolicybindings', None)
+ def clusterrolebindings(self):
+ if self._cluster_role_bindings is None:
+ results = self._get('clusterrolebindings', None)
if results['returncode'] != 0:
- raise OpenShiftCLIError('Could not retrieve clusterpolicybindings')
- self._cluster_policy_bindings = results['results'][0]['items'][0]
+ raise OpenShiftCLIError('Could not retrieve clusterrolebindings')
+ self._cluster_role_bindings = results['results'][0]['items']
- return self._cluster_policy_bindings
+ return self._cluster_role_bindings
@property
def role_binding(self):
@@ -99,18 +99,17 @@ class PolicyUser(OpenShiftCLI):
''' return whether role_binding exists '''
bindings = None
if self.config.config_options['resource_kind']['value'] == 'cluster-role':
- bindings = self.clusterpolicybindings
+ bindings = self.clusterrolebindings
else:
- bindings = self.policybindings
+ bindings = self.rolebindings
if bindings is None:
return False
- for binding in bindings['roleBindings']:
- _rb = binding['roleBinding']
- if _rb['roleRef']['name'] == self.config.config_options['name']['value'] and \
- _rb['userNames'] is not None and \
- self.config.config_options['user']['value'] in _rb['userNames']:
+ for binding in bindings:
+ if binding['roleRef']['name'] == self.config.config_options['name']['value'] and \
+ binding['userNames'] is not None and \
+ self.config.config_options['user']['value'] in binding['userNames']:
self.role_binding = binding
return True
diff --git a/roles/lib_openshift/src/class/oc_adm_registry.py b/roles/lib_openshift/src/class/oc_adm_registry.py
index 3c130fe28..ad6869bb6 100644
--- a/roles/lib_openshift/src/class/oc_adm_registry.py
+++ b/roles/lib_openshift/src/class/oc_adm_registry.py
@@ -143,7 +143,7 @@ class Registry(OpenShiftCLI):
def prepare_registry(self):
''' prepare a registry for instantiation '''
- options = self.config.to_option_list()
+ options = self.config.to_option_list(ascommalist='labels')
cmd = ['registry']
cmd.extend(options)
diff --git a/roles/lib_openshift/src/class/oc_adm_router.py b/roles/lib_openshift/src/class/oc_adm_router.py
index 1a0b94b80..0d50116d1 100644
--- a/roles/lib_openshift/src/class/oc_adm_router.py
+++ b/roles/lib_openshift/src/class/oc_adm_router.py
@@ -222,7 +222,7 @@ class Router(OpenShiftCLI):
# No certificate was passed to us. do not pass one to oc adm router
self.config.config_options['default_cert']['include'] = False
- options = self.config.to_option_list()
+ options = self.config.to_option_list(ascommalist='labels')
cmd = ['router', self.config.name]
cmd.extend(options)
diff --git a/roles/lib_openshift/src/class/oc_clusterrole.py b/roles/lib_openshift/src/class/oc_clusterrole.py
index ae6795446..328e5cb67 100644
--- a/roles/lib_openshift/src/class/oc_clusterrole.py
+++ b/roles/lib_openshift/src/class/oc_clusterrole.py
@@ -56,7 +56,7 @@ class OCClusterRole(OpenShiftCLI):
self.clusterrole = ClusterRole(content=result['results'][0])
result['results'] = self.clusterrole.yaml_dict
- elif 'clusterrole "{}" not found'.format(self.name) in result['stderr']:
+ elif '"{}" not found'.format(self.name) in result['stderr']:
result['returncode'] = 0
self.clusterrole = None
diff --git a/roles/lib_openshift/src/class/oc_obj.py b/roles/lib_openshift/src/class/oc_obj.py
index 6f0da3d5c..68f7818e4 100644
--- a/roles/lib_openshift/src/class/oc_obj.py
+++ b/roles/lib_openshift/src/class/oc_obj.py
@@ -33,7 +33,12 @@ class OCObject(OpenShiftCLI):
def delete(self):
'''delete the object'''
- return self._delete(self.kind, name=self.name, selector=self.selector)
+ results = self._delete(self.kind, name=self.name, selector=self.selector)
+ if (results['returncode'] != 0 and 'stderr' in results and
+ '\"{}\" not found'.format(self.name) in results['stderr']):
+ results['returncode'] = 0
+
+ return results
def create(self, files=None, content=None):
'''
@@ -45,7 +50,16 @@ class OCObject(OpenShiftCLI):
if files:
return self._create(files[0])
- content['data'] = yaml.dump(content['data'])
+ # pylint: disable=no-member
+ # The purpose of this change is twofold:
+ # - we need a check to only use the ruamel specific dumper if ruamel is loaded
+ # - the dumper or the flow style change is needed so openshift is able to parse
+ # the resulting yaml, at least until gopkg.in/yaml.v2 is updated
+ if hasattr(yaml, 'RoundTripDumper'):
+ content['data'] = yaml.dump(content['data'], Dumper=yaml.RoundTripDumper)
+ else:
+ content['data'] = yaml.safe_dump(content['data'], default_flow_style=False)
+
content_file = Utils.create_tmp_files_from_contents(content)[0]
return self._create(content_file['path'])
@@ -117,7 +131,8 @@ class OCObject(OpenShiftCLI):
if state == 'absent':
# verify its not in our results
if (params['name'] is not None or params['selector'] is not None) and \
- (len(api_rval['results']) == 0 or len(api_rval['results'][0].get('items', [])) == 0):
+ (len(api_rval['results']) == 0 or \
+ ('items' in api_rval['results'][0] and len(api_rval['results'][0]['items']) == 0)):
return {'changed': False, 'state': state}
if check_mode:
diff --git a/roles/lib_openshift/src/class/oc_process.py b/roles/lib_openshift/src/class/oc_process.py
index eba9a43cd..62a6bd571 100644
--- a/roles/lib_openshift/src/class/oc_process.py
+++ b/roles/lib_openshift/src/class/oc_process.py
@@ -30,7 +30,7 @@ class OCProcess(OpenShiftCLI):
if self._template is None:
results = self._process(self.name, False, self.params, self.data)
if results['returncode'] != 0:
- raise OpenShiftCLIError('Error processing template [%s].' % self.name)
+ raise OpenShiftCLIError('Error processing template [%s]: %s' %(self.name, results))
self._template = results['results']['items']
return self._template
diff --git a/roles/lib_openshift/src/class/oc_project.py b/roles/lib_openshift/src/class/oc_project.py
index 9ad8111a8..298597067 100644
--- a/roles/lib_openshift/src/class/oc_project.py
+++ b/roles/lib_openshift/src/class/oc_project.py
@@ -156,6 +156,9 @@ class OCProject(OpenShiftCLI):
# Create it here
api_rval = oadm_project.create()
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
# return the created object
api_rval = oadm_project.get()
diff --git a/roles/lib_openshift/src/class/oc_pvc.py b/roles/lib_openshift/src/class/oc_pvc.py
index c73abc47c..6b566c301 100644
--- a/roles/lib_openshift/src/class/oc_pvc.py
+++ b/roles/lib_openshift/src/class/oc_pvc.py
@@ -85,6 +85,8 @@ class OCPVC(OpenShiftCLI):
params['kubeconfig'],
params['access_modes'],
params['volume_capacity'],
+ params['selector'],
+ params['storage_class_name'],
)
oc_pvc = OCPVC(pconfig, verbose=params['debug'])
diff --git a/roles/lib_openshift/src/class/oc_route.py b/roles/lib_openshift/src/class/oc_route.py
index 3935525f1..dc2f7977b 100644
--- a/roles/lib_openshift/src/class/oc_route.py
+++ b/roles/lib_openshift/src/class/oc_route.py
@@ -68,9 +68,6 @@ class OCRoute(OpenShiftCLI):
@staticmethod
def get_cert_data(path, content):
'''get the data for a particular value'''
- if not path and not content:
- return None
-
rval = None
if path and os.path.exists(path) and os.access(path, os.R_OK):
rval = open(path).read()
@@ -109,18 +106,19 @@ class OCRoute(OpenShiftCLI):
if params['tls_termination'] and params['tls_termination'].lower() != 'passthrough': # E501
for key, option in files.items():
- if key == 'destcacert' and params['tls_termination'] != 'reencrypt':
+ if not option['path'] and not option['content']:
continue
option['value'] = OCRoute.get_cert_data(option['path'], option['content']) # E501
if not option['value']:
return {'failed': True,
- 'msg': 'Verify that you pass a value for %s' % key}
+ 'msg': 'Verify that you pass a correct value for %s' % key}
rconfig = RouteConfig(params['name'],
params['namespace'],
params['kubeconfig'],
+ params['labels'],
files['destcacert']['value'],
files['cacert']['value'],
files['cert']['value'],
diff --git a/roles/lib_openshift/src/class/oc_secret.py b/roles/lib_openshift/src/class/oc_secret.py
index deb36a9fa..89e70b6b2 100644
--- a/roles/lib_openshift/src/class/oc_secret.py
+++ b/roles/lib_openshift/src/class/oc_secret.py
@@ -13,12 +13,14 @@ class OCSecret(OpenShiftCLI):
def __init__(self,
namespace,
secret_name=None,
+ secret_type=None,
decode=False,
kubeconfig='/etc/origin/master/admin.kubeconfig',
verbose=False):
''' Constructor for OpenshiftOC '''
super(OCSecret, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose)
self.name = secret_name
+ self.type = secret_type
self.decode = decode
def get(self):
@@ -42,13 +44,17 @@ class OCSecret(OpenShiftCLI):
'''delete a secret by name'''
return self._delete('secrets', self.name)
- def create(self, files=None, contents=None):
+ def create(self, files=None, contents=None, force=False):
'''Create a secret '''
if not files:
files = Utils.create_tmp_files_from_contents(contents)
secrets = ["%s=%s" % (sfile['name'], sfile['path']) for sfile in files]
cmd = ['secrets', 'new', self.name]
+ if self.type is not None:
+ cmd.append("--type=%s" % (self.type))
+ if force:
+ cmd.append('--confirm')
cmd.extend(secrets)
results = self.openshift_cmd(cmd)
@@ -61,7 +67,7 @@ class OCSecret(OpenShiftCLI):
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)
+ secret = self.prep_secret(files, force=force)
if secret['returncode'] != 0:
return secret
@@ -73,7 +79,7 @@ class OCSecret(OpenShiftCLI):
return self._replace(sfile_path, force=force)
- def prep_secret(self, files=None, contents=None):
+ def prep_secret(self, files=None, contents=None, force=False):
''' return what the secret would look like if created
This is accomplished by passing -ojson. This will most likely change in the future
'''
@@ -82,6 +88,10 @@ class OCSecret(OpenShiftCLI):
secrets = ["%s=%s" % (sfile['name'], sfile['path']) for sfile in files]
cmd = ['-ojson', 'secrets', 'new', self.name]
+ if self.type is not None:
+ cmd.extend(["--type=%s" % (self.type)])
+ if force:
+ cmd.append('--confirm')
cmd.extend(secrets)
return self.openshift_cmd(cmd, output=True)
@@ -94,6 +104,7 @@ class OCSecret(OpenShiftCLI):
ocsecret = OCSecret(params['namespace'],
params['name'],
+ params['type'],
params['decode'],
kubeconfig=params['kubeconfig'],
verbose=params['debug'])
@@ -131,8 +142,7 @@ class OCSecret(OpenShiftCLI):
elif params['contents']:
files = Utils.create_tmp_files_from_contents(params['contents'])
else:
- return {'failed': True,
- 'msg': 'Either specify files or contents.'}
+ files = [{'name': 'null', 'path': os.devnull}]
########
# Create
@@ -143,7 +153,7 @@ class OCSecret(OpenShiftCLI):
return {'changed': True,
'msg': 'Would have performed a create.'}
- api_rval = ocsecret.create(files, params['contents'])
+ api_rval = ocsecret.create(files, params['contents'], force=params['force'])
# Remove files
if files and params['delete_after']:
@@ -160,7 +170,7 @@ class OCSecret(OpenShiftCLI):
########
# Update
########
- secret = ocsecret.prep_secret(params['files'], params['contents'])
+ secret = ocsecret.prep_secret(params['files'], params['contents'], force=params['force'])
if secret['returncode'] != 0:
return {'failed': True, 'msg': secret}
diff --git a/roles/lib_openshift/src/class/oc_service.py b/roles/lib_openshift/src/class/oc_service.py
index 20cf23df5..7268a0c88 100644
--- a/roles/lib_openshift/src/class/oc_service.py
+++ b/roles/lib_openshift/src/class/oc_service.py
@@ -19,13 +19,15 @@ class OCService(OpenShiftCLI):
ports,
session_affinity,
service_type,
+ external_ips,
kubeconfig='/etc/origin/master/admin.kubeconfig',
verbose=False):
''' Constructor for OCVolume '''
super(OCService, self).__init__(namespace, kubeconfig, verbose)
self.namespace = namespace
self.config = ServiceConfig(sname, namespace, ports, selector, labels,
- cluster_ip, portal_ip, session_affinity, service_type)
+ cluster_ip, portal_ip, session_affinity, service_type,
+ external_ips)
self.user_svc = Service(content=self.config.data)
self.svc = None
@@ -94,6 +96,7 @@ class OCService(OpenShiftCLI):
params['ports'],
params['session_affinity'],
params['service_type'],
+ params['external_ips'],
params['kubeconfig'],
params['debug'])
diff --git a/roles/lib_openshift/src/class/oc_storageclass.py b/roles/lib_openshift/src/class/oc_storageclass.py
new file mode 100644
index 000000000..aced586ae
--- /dev/null
+++ b/roles/lib_openshift/src/class/oc_storageclass.py
@@ -0,0 +1,155 @@
+# pylint: skip-file
+# flake8: noqa
+
+# pylint: disable=too-many-instance-attributes
+class OCStorageClass(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools '''
+ kind = 'storageclass'
+
+ # pylint allows 5
+ # pylint: disable=too-many-arguments
+ def __init__(self,
+ config,
+ verbose=False):
+ ''' Constructor for OCStorageClass '''
+ super(OCStorageClass, self).__init__(None, kubeconfig=config.kubeconfig, verbose=verbose)
+ self.config = config
+ self.storage_class = None
+
+ def exists(self):
+ ''' return whether a storageclass exists'''
+ if self.storage_class:
+ return True
+
+ return False
+
+ def get(self):
+ '''return storageclass '''
+ result = self._get(self.kind, self.config.name)
+ if result['returncode'] == 0:
+ self.storage_class = StorageClass(content=result['results'][0])
+ elif '\"%s\" not found' % self.config.name in result['stderr']:
+ result['returncode'] = 0
+ result['results'] = [{}]
+
+ return result
+
+ def delete(self):
+ '''delete the object'''
+ return self._delete(self.kind, self.config.name)
+
+ def create(self):
+ '''create the object'''
+ return self._create_from_content(self.config.name, self.config.data)
+
+ def update(self):
+ '''update the object'''
+ # parameters are currently unable to be updated. need to delete and recreate
+ self.delete()
+ # pause here and attempt to wait for delete.
+ # Better option would be to poll
+ import time
+ time.sleep(5)
+ return self.create()
+
+ def needs_update(self):
+ ''' verify an update is needed '''
+ # check if params have updated
+ if self.storage_class.get_parameters() != self.config.parameters:
+ return True
+
+ for anno_key, anno_value in self.storage_class.get_annotations().items():
+ if 'is-default-class' in anno_key and anno_value != self.config.default_storage_class:
+ return True
+
+ return False
+
+ @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'''
+
+ rconfig = StorageClassConfig(params['name'],
+ provisioner="kubernetes.io/{}".format(params['provisioner']),
+ parameters=params['parameters'],
+ annotations=params['annotations'],
+ api_version="storage.k8s.io/{}".format(params['api_version']),
+ default_storage_class=params.get('default_storage_class', 'false'),
+ kubeconfig=params['kubeconfig'],
+ )
+
+ oc_sc = OCStorageClass(rconfig, verbose=params['debug'])
+
+ state = params['state']
+
+ api_rval = oc_sc.get()
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ return {'changed': False, 'results': api_rval['results'], 'state': 'list'}
+
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ if oc_sc.exists():
+
+ if check_mode:
+ return {'changed': True, 'msg': 'Would have performed a delete.'}
+
+ api_rval = oc_sc.delete()
+
+ return {'changed': True, 'results': api_rval, 'state': 'absent'}
+
+ return {'changed': False, 'state': 'absent'}
+
+ if state == 'present':
+ ########
+ # Create
+ ########
+ if not oc_sc.exists():
+
+ if check_mode:
+ return {'changed': True, 'msg': 'Would have performed a create.'}
+
+ # Create it here
+ api_rval = oc_sc.create()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ # return the created object
+ api_rval = oc_sc.get()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ return {'changed': True, 'results': api_rval, 'state': 'present'}
+
+ ########
+ # Update
+ ########
+ if oc_sc.needs_update():
+ api_rval = oc_sc.update()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ # return the created object
+ api_rval = oc_sc.get()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ return {'changed': True, 'results': api_rval, 'state': 'present'}
+
+ return {'changed': False, 'results': api_rval, 'state': 'present'}
+
+
+ return {'failed': True,
+ 'changed': False,
+ 'msg': 'Unknown state passed. %s' % state,
+ 'state': 'unknown'}
diff --git a/roles/lib_openshift/src/doc/csr b/roles/lib_openshift/src/doc/csr
new file mode 100644
index 000000000..db72dbda3
--- /dev/null
+++ b/roles/lib_openshift/src/doc/csr
@@ -0,0 +1,80 @@
+# flake8: noqa
+# pylint: skip-file
+
+DOCUMENTATION = '''
+---
+module: oc_adm_csr
+short_description: Module to approve or deny openshift certificate signing requests
+description:
+ - Wrapper around the openshift `oc adm certificate approve|deny <csr>` command.
+options:
+ state:
+ description:
+ - approve|deny|list Approve, deny, and list are the only supported states for certificates
+ required: false
+ default: present
+ choices:
+ - present
+ 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: []
+ nodes:
+ description:
+ - A list of the names of the nodes in which to accept the certificates
+ required: false
+ default: None
+ aliases: []
+ timeout:
+ description:
+ - This flag allows for a timeout value when approving nodes.
+ required: false
+ default: 30
+ aliases: []
+ timeout:
+ description:
+ - This flag allows for a timeout value when doing node approvals.
+ - A zero value for the timeout will block until the nodes have been accepted
+ required: false
+ default: 30
+ aliases: []
+ approve_all:
+ description:
+ - This flag allows for the module to approve all CSRs that are found.
+ - This facilitates testing.
+ required: false
+ default: False
+ aliases: []
+ service_account:
+ description:
+ - This parameter tells the approval process which service account is being used for the requests
+ required: false
+ default: node-bootstrapper
+ aliases: []
+author:
+- "Kenny Woodson <kwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+- name: Approve certificates for node xyz
+ oc_adm_scr:
+ nodes:
+ - xyz
+ timeout: 300
+
+- name: Approve certificates for node xyz
+ oc_adm_scr:
+ nodes:
+ - xyz
+ timeout: 0
+'''
diff --git a/roles/lib_openshift/src/doc/obj b/roles/lib_openshift/src/doc/obj
index 4ff912b2d..c6504ed01 100644
--- a/roles/lib_openshift/src/doc/obj
+++ b/roles/lib_openshift/src/doc/obj
@@ -39,9 +39,9 @@ options:
required: false
default: str
aliases: []
- all_namespace:
+ all_namespaces:
description:
- - The namespace where the object lives.
+ - Search in all namespaces for the object.
required: false
default: false
aliases: []
diff --git a/roles/lib_openshift/src/doc/pvc b/roles/lib_openshift/src/doc/pvc
index 9240f2a0f..268ad0b94 100644
--- a/roles/lib_openshift/src/doc/pvc
+++ b/roles/lib_openshift/src/doc/pvc
@@ -59,6 +59,18 @@ options:
- ReadOnlyMany
- ReadWriteMany
aliases: []
+ storage_class_name:
+ description:
+ - The storage class name for the PVC
+ required: false
+ default: None
+ aliases: []
+ selector:
+ description:
+ - A hash of key/values for the matchLabels
+ required: false
+ default: None
+ aliases: []
author:
- "Kenny Woodson <kwoodson@redhat.com>"
extends_documentation_fragment: []
diff --git a/roles/lib_openshift/src/doc/route b/roles/lib_openshift/src/doc/route
index a12999c9e..f0d38ab5f 100644
--- a/roles/lib_openshift/src/doc/route
+++ b/roles/lib_openshift/src/doc/route
@@ -39,6 +39,12 @@ options:
required: false
default: str
aliases: []
+ labels:
+ description:
+ - The labels to apply on the route
+ required: false
+ default: None
+ aliases: []
tls_termination:
description:
- The options for termination. e.g. reencrypt
diff --git a/roles/lib_openshift/src/doc/secret b/roles/lib_openshift/src/doc/secret
index 5c2bd9bc0..a27f90f38 100644
--- a/roles/lib_openshift/src/doc/secret
+++ b/roles/lib_openshift/src/doc/secret
@@ -39,6 +39,12 @@ options:
required: false
default: default
aliases: []
+ annotations:
+ description:
+ - Annotations to apply to the object
+ required: false
+ default: None
+ aliases: []
files:
description:
- A list of files provided for secrets
@@ -57,6 +63,12 @@ options:
required: false
default: None
aliases: []
+ type:
+ description:
+ - The secret type.
+ required: false
+ default: None
+ aliases: []
force:
description:
- Whether or not to force the operation
diff --git a/roles/lib_openshift/src/doc/service b/roles/lib_openshift/src/doc/service
index 418f91dc5..ba9aa0b38 100644
--- a/roles/lib_openshift/src/doc/service
+++ b/roles/lib_openshift/src/doc/service
@@ -89,6 +89,13 @@ options:
- LoadBalancer
- ExternalName
aliases: []
+ externalips:
+ description:
+ - A list of the external IPs that are exposed for this service.
+ - https://kubernetes.io/docs/concepts/services-networking/service/#external-ips
+ required: false
+ default: None
+ aliases: []
author:
- "Kenny Woodson <kwoodson@redhat.com>"
extends_documentation_fragment: []
diff --git a/roles/lib_openshift/src/doc/storageclass b/roles/lib_openshift/src/doc/storageclass
new file mode 100644
index 000000000..5a7320d55
--- /dev/null
+++ b/roles/lib_openshift/src/doc/storageclass
@@ -0,0 +1,86 @@
+# flake8: noqa
+# pylint: skip-file
+
+DOCUMENTATION = '''
+---
+module: oc_storageclass
+short_description: Create, modify, and idempotently manage openshift storageclasses.
+description:
+ - Manage openshift storageclass objects programmatically.
+options:
+ state:
+ description:
+ - State represents whether to create, modify, delete, or list
+ required: False
+ default: present
+ choices: ["present", "absent", "list"]
+ aliases: []
+ kubeconfig:
+ description:
+ - The path for the kubeconfig file to use for authentication
+ required: false
+ default: /etc/origin/master/admin.kubeconfig
+ aliases: []
+ debug:
+ description:
+ - Turn on debug output.
+ required: false
+ default: False
+ aliases: []
+ name:
+ description:
+ - Name of the object that is being queried.
+ required: false
+ default: None
+ aliases: []
+ provisioner:
+ description:
+ - Any annotations to add to the storageclass
+ required: false
+ default: 'aws-ebs'
+ aliases: []
+ default_storage_class:
+ description:
+ - Whether or not this is the default storage class
+ required: false
+ default: False
+ aliases: []
+ parameters:
+ description:
+ - A dictionary with the parameters to configure the storageclass. This will be based on provisioner
+ required: false
+ default: None
+ aliases: []
+ api_version:
+ description:
+ - The api version.
+ required: false
+ default: v1
+ aliases: []
+author:
+- "Kenny Woodson <kwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+- name: get storageclass
+ run_once: true
+ oc_storageclass:
+ name: gp2
+ state: list
+ register: registry_sc_out
+
+- name: create the storageclass
+ oc_storageclass:
+ run_once: true
+ name: gp2
+ parameters:
+ type: gp2
+ encrypted: 'true'
+ kmsKeyId: '<full kms key arn>'
+ provisioner: aws-ebs
+ default_storage_class: False
+ register: sc_out
+ notify:
+ - restart openshift master services
+'''
diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py
index 2bf795e25..1fb32164e 100644
--- a/roles/lib_openshift/src/lib/base.py
+++ b/roles/lib_openshift/src/lib/base.py
@@ -128,7 +128,7 @@ class OpenShiftCLI(object):
else:
cmd.append(template_name)
if params:
- param_str = ["{}={}".format(key, value) for key, value in params.items()]
+ param_str = ["{}={}".format(key, str(value).replace("'", r'"')) for key, value in params.items()]
cmd.append('-v')
cmd.extend(param_str)
@@ -273,10 +273,6 @@ class OpenShiftCLI(object):
elif self.namespace is not None and self.namespace.lower() not in ['none', 'emtpy']: # E501
cmds.extend(['-n', self.namespace])
- rval = {}
- results = ''
- err = None
-
if self.verbose:
print(' '.join(cmds))
@@ -286,34 +282,26 @@ class OpenShiftCLI(object):
returncode, stdout, stderr = 1, '', 'Failed to execute {}: {}'.format(subprocess.list2cmdline(cmds), ex)
rval = {"returncode": returncode,
- "results": results,
"cmd": ' '.join(cmds)}
- if returncode == 0:
- if output:
- if output_type == 'json':
- try:
- rval['results'] = json.loads(stdout)
- except ValueError as verr:
- if "No JSON object could be decoded" in verr.args:
- err = verr.args
- elif output_type == 'raw':
- rval['results'] = stdout
-
- if self.verbose:
- print("STDOUT: {0}".format(stdout))
- print("STDERR: {0}".format(stderr))
-
- if err:
- rval.update({"err": err,
- "stderr": stderr,
- "stdout": stdout,
- "cmd": cmds})
+ if output_type == 'json':
+ rval['results'] = {}
+ if output and stdout:
+ try:
+ rval['results'] = json.loads(stdout)
+ except ValueError as verr:
+ if "No JSON object could be decoded" in verr.args:
+ rval['err'] = verr.args
+ elif output_type == 'raw':
+ rval['results'] = stdout if output else ''
- else:
+ if self.verbose:
+ print("STDOUT: {0}".format(stdout))
+ print("STDERR: {0}".format(stderr))
+
+ if 'err' in rval or returncode != 0:
rval.update({"stderr": stderr,
- "stdout": stdout,
- "results": {}})
+ "stdout": stdout})
return rval
@@ -476,13 +464,12 @@ class Utils(object):
@staticmethod
def openshift_installed():
''' check if openshift is installed '''
- import yum
+ import rpm
- yum_base = yum.YumBase()
- if yum_base.rpmdb.searchNevra(name='atomic-openshift'):
- return True
+ transaction_set = rpm.TransactionSet()
+ rpmquery = transaction_set.dbMatch("name", "atomic-openshift")
- return False
+ return rpmquery.count() > 0
# Disabling too-many-branches. This is a yaml dictionary comparison function
# pylint: disable=too-many-branches,too-many-return-statements,too-many-statements
@@ -581,7 +568,6 @@ class Utils(object):
print('returning true')
return True
-
class OpenShiftCLIConfig(object):
'''Generic Config'''
def __init__(self, rname, namespace, kubeconfig, options):
@@ -595,18 +581,28 @@ class OpenShiftCLIConfig(object):
''' return config options '''
return self._options
- def to_option_list(self):
- '''return all options as a string'''
- return self.stringify()
-
- def stringify(self):
- ''' return the options hash as cli params in a string '''
+ def to_option_list(self, ascommalist=''):
+ '''return all options as a string
+ if ascommalist is set to the name of a key, and
+ the value of that key is a dict, format the dict
+ as a list of comma delimited key=value pairs'''
+ return self.stringify(ascommalist)
+
+ def stringify(self, ascommalist=''):
+ ''' return the options hash as cli params in a string
+ if ascommalist is set to the name of a key, and
+ the value of that key is a dict, format the dict
+ as a list of comma delimited key=value pairs '''
rval = []
for key in sorted(self.config_options.keys()):
data = self.config_options[key]
if data['include'] \
- and (data['value'] or isinstance(data['value'], int)):
- rval.append('--{}={}'.format(key.replace('_', '-'), data['value']))
+ and (data['value'] is not None or isinstance(data['value'], int)):
+ if key == ascommalist:
+ val = ','.join(['{}={}'.format(kk, vv) for kk, vv in sorted(data['value'].items())])
+ else:
+ val = data['value']
+ rval.append('--{}={}'.format(key.replace('_', '-'), val))
return rval
diff --git a/roles/lib_openshift/src/lib/pvc.py b/roles/lib_openshift/src/lib/pvc.py
index 929b50990..d1e935c32 100644
--- a/roles/lib_openshift/src/lib/pvc.py
+++ b/roles/lib_openshift/src/lib/pvc.py
@@ -11,7 +11,9 @@ class PersistentVolumeClaimConfig(object):
namespace,
kubeconfig,
access_modes=None,
- vol_capacity='1G'):
+ vol_capacity='1G',
+ selector=None,
+ storage_class_name=None):
''' constructor for handling pvc options '''
self.kubeconfig = kubeconfig
self.name = sname
@@ -19,6 +21,8 @@ class PersistentVolumeClaimConfig(object):
self.access_modes = access_modes
self.vol_capacity = vol_capacity
self.data = {}
+ self.selector = selector
+ self.storage_class_name = storage_class_name
self.create_dict()
@@ -36,12 +40,16 @@ class PersistentVolumeClaimConfig(object):
self.data['spec']['accessModes'] = ['ReadWriteOnce']
if self.access_modes:
self.data['spec']['accessModes'] = self.access_modes
+ if self.selector:
+ self.data['spec']['selector'] = {'matchLabels': self.selector}
# storage capacity
self.data['spec']['resources'] = {}
self.data['spec']['resources']['requests'] = {}
self.data['spec']['resources']['requests']['storage'] = self.vol_capacity
+ if self.storage_class_name:
+ self.data['spec']['storageClassName'] = self.storage_class_name
# pylint: disable=too-many-instance-attributes,too-many-public-methods
class PersistentVolumeClaim(Yedit):
@@ -51,13 +59,29 @@ class PersistentVolumeClaim(Yedit):
volume_name_path = "spec.volumeName"
bound_path = "status.phase"
kind = 'PersistentVolumeClaim'
+ selector_path = "spec.selector.matchLabels"
+ storage_class_name_path = "spec.storageClassName"
def __init__(self, content):
- '''RoleBinding constructor'''
+ '''PersistentVolumeClaim constructor'''
super(PersistentVolumeClaim, self).__init__(content=content)
self._access_modes = None
self._volume_capacity = None
self._volume_name = None
+ self._selector = None
+ self._storage_class_name = None
+
+ @property
+ def storage_class_name(self):
+ ''' storage_class_name property '''
+ if self._storage_class_name is None:
+ self._storage_class_name = self.get_storage_class_name()
+ return self._storage_class_name
+
+ @storage_class_name.setter
+ def storage_class_name(self, data):
+ ''' storage_class_name property setter'''
+ self._storage_class_name = data
@property
def volume_name(self):
@@ -72,6 +96,24 @@ class PersistentVolumeClaim(Yedit):
self._volume_name = data
@property
+ def selector(self):
+ ''' selector property '''
+ if self._selector is None:
+ self._selector = self.get_selector()
+ if not isinstance(self._selector, dict):
+ self._selector = dict(self._selector)
+
+ return self._selector
+
+ @selector.setter
+ def selector(self, data):
+ ''' selector property setter'''
+ if not isinstance(data, dict):
+ data = dict(data)
+
+ self._selector = data
+
+ @property
def access_modes(self):
''' access_modes property '''
if self._access_modes is None:
@@ -101,6 +143,14 @@ class PersistentVolumeClaim(Yedit):
''' volume_capacity property setter'''
self._volume_capacity = data
+ def get_storage_class_name(self):
+ '''get storage_class_name'''
+ return self.get(PersistentVolumeClaim.storage_class_name_path) or []
+
+ def get_selector(self):
+ '''get selector'''
+ return self.get(PersistentVolumeClaim.selector_path) or []
+
def get_access_modes(self):
'''get access_modes'''
return self.get(PersistentVolumeClaim.access_modes_path) or []
diff --git a/roles/lib_openshift/src/lib/route.py b/roles/lib_openshift/src/lib/route.py
index 3b54a24fb..b106866cb 100644
--- a/roles/lib_openshift/src/lib/route.py
+++ b/roles/lib_openshift/src/lib/route.py
@@ -11,6 +11,7 @@ class RouteConfig(object):
sname,
namespace,
kubeconfig,
+ labels=None,
destcacert=None,
cacert=None,
cert=None,
@@ -25,6 +26,7 @@ class RouteConfig(object):
self.kubeconfig = kubeconfig
self.name = sname
self.namespace = namespace
+ self.labels = labels
self.host = host
self.tls_termination = tls_termination
self.destcacert = destcacert
@@ -50,6 +52,8 @@ class RouteConfig(object):
self.data['metadata'] = {}
self.data['metadata']['name'] = self.name
self.data['metadata']['namespace'] = self.namespace
+ if self.labels:
+ self.data['metadata']['labels'] = self.labels
self.data['spec'] = {}
self.data['spec']['host'] = self.host
diff --git a/roles/lib_openshift/src/lib/secret.py b/roles/lib_openshift/src/lib/secret.py
index 75c32e8b1..ad4b6aa36 100644
--- a/roles/lib_openshift/src/lib/secret.py
+++ b/roles/lib_openshift/src/lib/secret.py
@@ -9,12 +9,16 @@ class SecretConfig(object):
sname,
namespace,
kubeconfig,
- secrets=None):
+ secrets=None,
+ stype=None,
+ annotations=None):
''' constructor for handling secret options '''
self.kubeconfig = kubeconfig
self.name = sname
+ self.type = stype
self.namespace = namespace
self.secrets = secrets
+ self.annotations = annotations
self.data = {}
self.create_dict()
@@ -23,6 +27,7 @@ class SecretConfig(object):
''' assign the correct properties for a secret dict '''
self.data['apiVersion'] = 'v1'
self.data['kind'] = 'Secret'
+ self.data['type'] = self.type
self.data['metadata'] = {}
self.data['metadata']['name'] = self.name
self.data['metadata']['namespace'] = self.namespace
@@ -30,6 +35,8 @@ class SecretConfig(object):
if self.secrets:
for key, value in self.secrets.items():
self.data['data'][key] = value
+ if self.annotations:
+ self.data['metadata']['annotations'] = self.annotations
# pylint: disable=too-many-instance-attributes
class Secret(Yedit):
diff --git a/roles/lib_openshift/src/lib/service.py b/roles/lib_openshift/src/lib/service.py
index eef568779..0e8cc3aa5 100644
--- a/roles/lib_openshift/src/lib/service.py
+++ b/roles/lib_openshift/src/lib/service.py
@@ -15,7 +15,8 @@ class ServiceConfig(object):
cluster_ip=None,
portal_ip=None,
session_affinity=None,
- service_type=None):
+ service_type=None,
+ external_ips=None):
''' constructor for handling service options '''
self.name = sname
self.namespace = namespace
@@ -26,6 +27,7 @@ class ServiceConfig(object):
self.portal_ip = portal_ip
self.session_affinity = session_affinity
self.service_type = service_type
+ self.external_ips = external_ips
self.data = {}
self.create_dict()
@@ -38,8 +40,9 @@ class ServiceConfig(object):
self.data['metadata']['name'] = self.name
self.data['metadata']['namespace'] = self.namespace
if self.labels:
- for lab, lab_value in self.labels.items():
- self.data['metadata'][lab] = lab_value
+ self.data['metadata']['labels'] = {}
+ for lab, lab_value in self.labels.items():
+ self.data['metadata']['labels'][lab] = lab_value
self.data['spec'] = {}
if self.ports:
@@ -61,6 +64,10 @@ class ServiceConfig(object):
if self.service_type:
self.data['spec']['type'] = self.service_type
+ if self.external_ips:
+ self.data['spec']['externalIPs'] = self.external_ips
+
+
# pylint: disable=too-many-instance-attributes,too-many-public-methods
class Service(Yedit):
''' Class to model the oc service object '''
@@ -69,6 +76,7 @@ class Service(Yedit):
cluster_ip = "spec.clusterIP"
selector_path = 'spec.selector'
kind = 'Service'
+ external_ips = "spec.externalIPs"
def __init__(self, content):
'''Service constructor'''
@@ -129,3 +137,50 @@ class Service(Yedit):
def add_portal_ip(self, pip):
'''add cluster ip'''
self.put(Service.portal_ip, pip)
+
+ def get_external_ips(self):
+ ''' get a list of external_ips '''
+ return self.get(Service.external_ips) or []
+
+ def add_external_ips(self, inc_external_ips):
+ ''' add an external_ip to the external_ips list '''
+ if not isinstance(inc_external_ips, list):
+ inc_external_ips = [inc_external_ips]
+
+ external_ips = self.get_external_ips()
+ if not external_ips:
+ self.put(Service.external_ips, inc_external_ips)
+ else:
+ external_ips.extend(inc_external_ips)
+
+ return True
+
+ def find_external_ips(self, inc_external_ip):
+ ''' find a specific external IP '''
+ val = None
+ try:
+ idx = self.get_external_ips().index(inc_external_ip)
+ val = self.get_external_ips()[idx]
+ except ValueError:
+ pass
+
+ return val
+
+ def delete_external_ips(self, inc_external_ips):
+ ''' remove an external IP from a service '''
+ if not isinstance(inc_external_ips, list):
+ inc_external_ips = [inc_external_ips]
+
+ external_ips = self.get(Service.external_ips) or []
+
+ if not external_ips:
+ return True
+
+ removed = False
+ for inc_external_ip in inc_external_ips:
+ external_ip = self.find_external_ips(inc_external_ip)
+ if external_ip:
+ external_ips.remove(external_ip)
+ removed = True
+
+ return removed
diff --git a/roles/lib_openshift/src/lib/storageclass.py b/roles/lib_openshift/src/lib/storageclass.py
new file mode 100644
index 000000000..c49a3066a
--- /dev/null
+++ b/roles/lib_openshift/src/lib/storageclass.py
@@ -0,0 +1,73 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+# pylint: disable=too-many-instance-attributes
+class StorageClassConfig(object):
+ ''' Handle service options '''
+ # pylint: disable=too-many-arguments
+ def __init__(self,
+ name,
+ provisioner,
+ parameters=None,
+ annotations=None,
+ default_storage_class="false",
+ api_version='v1',
+ kubeconfig='/etc/origin/master/admin.kubeconfig'):
+ ''' constructor for handling storageclass options '''
+ self.name = name
+ self.parameters = parameters
+ self.annotations = annotations
+ self.provisioner = provisioner
+ self.api_version = api_version
+ self.default_storage_class = str(default_storage_class).lower()
+ self.kubeconfig = kubeconfig
+ self.data = {}
+
+ self.create_dict()
+
+ def create_dict(self):
+ ''' instantiates a storageclass dict '''
+ self.data['apiVersion'] = self.api_version
+ self.data['kind'] = 'StorageClass'
+ self.data['metadata'] = {}
+ self.data['metadata']['name'] = self.name
+
+ self.data['metadata']['annotations'] = {}
+ if self.annotations is not None:
+ self.data['metadata']['annotations'] = self.annotations
+
+ self.data['metadata']['annotations']['storageclass.beta.kubernetes.io/is-default-class'] = \
+ self.default_storage_class
+
+ self.data['provisioner'] = self.provisioner
+
+ self.data['parameters'] = {}
+ if self.parameters is not None:
+ self.data['parameters'].update(self.parameters)
+
+ # default to aws if no params were passed
+ else:
+ self.data['parameters']['type'] = 'gp2'
+
+
+
+# pylint: disable=too-many-instance-attributes,too-many-public-methods
+class StorageClass(Yedit):
+ ''' Class to model the oc storageclass object '''
+ annotations_path = "metadata.annotations"
+ provisioner_path = "provisioner"
+ parameters_path = "parameters"
+ kind = 'StorageClass'
+
+ def __init__(self, content):
+ '''StorageClass constructor'''
+ super(StorageClass, self).__init__(content=content)
+
+ def get_annotations(self):
+ ''' get a list of ports '''
+ return self.get(StorageClass.annotations_path) or {}
+
+ def get_parameters(self):
+ ''' get the service selector'''
+ return self.get(StorageClass.parameters_path) or {}
diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml
index 9fa2a6c0e..4636f6b9b 100644
--- a/roles/lib_openshift/src/sources.yml
+++ b/roles/lib_openshift/src/sources.yml
@@ -9,6 +9,16 @@ oc_adm_ca_server_cert.py:
- class/oc_adm_ca_server_cert.py
- ansible/oc_adm_ca_server_cert.py
+oc_adm_csr.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/csr
+- ../../lib_utils/src/class/yedit.py
+- lib/base.py
+- class/oc_adm_csr.py
+- ansible/oc_adm_csr.py
+
oc_adm_manage_node.py:
- doc/generated
- doc/license
@@ -263,6 +273,17 @@ oc_service.py:
- class/oc_service.py
- ansible/oc_service.py
+oc_storageclass.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/storageclass
+- ../../lib_utils/src/class/yedit.py
+- lib/base.py
+- lib/storageclass.py
+- class/oc_storageclass.py
+- ansible/oc_storageclass.py
+
oc_user.py:
- doc/generated
- doc/license
diff --git a/roles/lib_openshift/src/test/integration/filter_plugins/filters.py b/roles/lib_openshift/src/test/integration/filter_plugins/test_filters.py
index f350bd25d..f350bd25d 100644
--- a/roles/lib_openshift/src/test/integration/filter_plugins/filters.py
+++ b/roles/lib_openshift/src/test/integration/filter_plugins/test_filters.py
diff --git a/roles/lib_openshift/src/test/integration/oc_adm_csr.yml b/roles/lib_openshift/src/test/integration/oc_adm_csr.yml
new file mode 100755
index 000000000..cad8e36f5
--- /dev/null
+++ b/roles/lib_openshift/src/test/integration/oc_adm_csr.yml
@@ -0,0 +1,28 @@
+#!/usr/bin/ansible-playbook --module-path=../../../library/
+# ./oc_adm_csr.yml -M ../../../library -e "cli_master_test=$OPENSHIFT_MASTER
+---
+- hosts: masters
+ gather_facts: no
+ user: root
+ tasks:
+ - name: list csrs
+ oc_adm_csr:
+ state: list
+ register: csrout
+
+ - debug: var=csrout
+
+ - name: list csrs
+ oc_adm_csr:
+ state: approve
+ nodes:
+ - ip-172-31-51-0-ec2-internal
+ - ip-172-31-51-246-ec2-internal
+ - ip-172-31-54-12-ec2-internal
+ - ip-172-31-58-173-ec2-internal
+ - ip-172-31-58-212-ec2-internal
+ - ip-172-31-51-246-ec2-internal
+ - ip-172-31-54-12-ec2-internal
+
+ register: csrout
+ - debug: var=csrout
diff --git a/roles/lib_openshift/src/test/integration/oc_configmap.yml b/roles/lib_openshift/src/test/integration/oc_configmap.yml
index c0d200e73..6a452ccec 100755
--- a/roles/lib_openshift/src/test/integration/oc_configmap.yml
+++ b/roles/lib_openshift/src/test/integration/oc_configmap.yml
@@ -55,7 +55,7 @@
config: "{{ filename }}"
from_literal:
foo: notbar
- deployment_type: online
+ deployment_type: openshift-enterprise
- name: fetch the updated configmap
oc_configmap:
@@ -70,7 +70,7 @@
assert:
that:
- cmout.results.results[0].metadata.name == 'configmaptest'
- - cmout.results.results[0].data.deployment_type == 'online'
+ - cmout.results.results[0].data.deployment_type == 'openshift-enterprise'
- cmout.results.results[0].data.foo == 'notbar'
###### end update test ###########
diff --git a/roles/lib_openshift/src/test/integration/oc_pvc.yml b/roles/lib_openshift/src/test/integration/oc_pvc.yml
new file mode 100755
index 000000000..fb3a4781f
--- /dev/null
+++ b/roles/lib_openshift/src/test/integration/oc_pvc.yml
@@ -0,0 +1,28 @@
+#!/usr/bin/ansible-playbook --module-path=../../../library/
+# ./oc_pvc.yml -e "cli_master_test=$OPENSHIFT_MASTER
+---
+- hosts: "{{ cli_master_test }}"
+ gather_facts: no
+ user: root
+ tasks:
+ - name: create pvc
+ oc_pvc:
+ state: present
+ name: oc-pvc-create-test
+ namespace: default
+ volume_capacity: 3G
+ access_modes:
+ - ReadWriteOnce
+ selector:
+ foo: bar
+ storage_class_name: my-storage-class-name
+ register: pvcout
+ - debug: var=pvcout
+
+ - assert:
+ that:
+ - pvcout.results.results[0]['metadata']['name'] == 'oc-pvc-create-test'
+ - pvcout.results.results[0]['spec']['storageClassName'] == 'my-storage-class-name'
+ - pvcout.results.results[0]['spec']['selector']['matchLabels']['foo'] == 'bar'
+ - pvcout.changed
+ msg: pvc create failed.
diff --git a/roles/lib_openshift/src/test/integration/oc_service.yml b/roles/lib_openshift/src/test/integration/oc_service.yml
index 3eb6facef..29535f24a 100755
--- a/roles/lib_openshift/src/test/integration/oc_service.yml
+++ b/roles/lib_openshift/src/test/integration/oc_service.yml
@@ -18,6 +18,9 @@
test-registtry: default
session_affinity: ClientIP
service_type: ClusterIP
+ labels:
+ component: test-registry
+ infra: registry
register: svc_out
- debug: var=svc_out
@@ -25,6 +28,8 @@
that:
- "svc_out.results.results[0]['metadata']['name'] == 'test-registry'"
- svc_out.changed
+ - "svc_out.results.results[0]['metadata']['labels']['component'] == 'test-registry'"
+ - "svc_out.results.results[0]['metadata']['labels']['infra'] == 'registry'"
msg: service create failed.
# Test idempotent create
diff --git a/roles/lib_openshift/src/test/integration/oc_storageclass.yml b/roles/lib_openshift/src/test/integration/oc_storageclass.yml
new file mode 100755
index 000000000..c82f9dedb
--- /dev/null
+++ b/roles/lib_openshift/src/test/integration/oc_storageclass.yml
@@ -0,0 +1,87 @@
+#!/usr/bin/ansible-playbook --module-path=../../../library/
+# ./oc_storageclass.yml -M ../../../library -e "cli_master_test=$OPENSHIFT_MASTER
+---
+- hosts: "{{ cli_master_test }}"
+ gather_facts: no
+ user: root
+ tasks:
+ - name: create a storageclass
+ oc_storageclass:
+ name: testsc
+ parameters:
+ type: gp2
+ default_storage_class: "true"
+ register: sc_out
+ - debug: var=sc_out
+
+ - assert:
+ that:
+ - "sc_out.results.results[0]['metadata']['name'] == 'testsc'"
+ - sc_out.changed
+ - "sc_out.results.results[0]['parameters']['type'] == 'gp2'"
+ msg: storageclass create failed.
+
+ # Test idempotent create
+ - name: NOOP create the storageclass
+ oc_storageclass:
+ name: testsc
+ parameters:
+ type: gp2
+ default_storage_class: "true"
+ register: sc_out
+
+ - assert:
+ that:
+ - "sc_out.results.results[0]['metadata']['name'] == 'testsc'"
+ - sc_out.changed == False
+ msg: storageclass create failed. No changes expected
+
+ - name: test list storageclass
+ oc_storageclass:
+ name: testsc
+ state: list
+ register: sc_out
+ - debug: var=sc_out
+
+ - assert:
+ that: "sc_out.results[0]['metadata']['name'] == 'testsc'"
+ msg: storageclass list failed
+
+ - name: update the storageclass
+ oc_storageclass:
+ name: testsc
+ parameters:
+ type: gp2
+ encrypted: "true"
+ default_storage_class: "true"
+ register: sc_out
+
+ - assert:
+ that: "sc_out.results.results[0]['parameters']['encrypted'] == 'true'"
+ msg: storageclass update failed
+
+ - name: oc delete storageclass
+ oc_storageclass:
+ name: testsc
+ state: absent
+ register: sc_out
+ - debug: var=sc_out
+
+ - assert:
+ that:
+ - "sc_out.results['returncode'] == 0"
+ - "sc_out.results.results == {}"
+ msg: storageclass delete failed
+
+ - name: oc get storageclass
+ oc_storageclass:
+ name: testsc
+ state: list
+ register: sc_out
+ - debug: var=sc_out
+
+ - assert:
+ that:
+ - sc_out.changed == False
+ - "sc_out.results == [{}]"
+ msg: storageclass get failed
diff --git a/roles/lib_openshift/src/test/unit/test_oc_adm_registry.py b/roles/lib_openshift/src/test/unit/test_oc_adm_registry.py
index 97cf86170..77787fe87 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_adm_registry.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_adm_registry.py
@@ -218,7 +218,7 @@ class RegistryTest(unittest.TestCase):
'kubeconfig': '/etc/origin/master/admin.kubeconfig',
'images': None,
'latest_images': None,
- 'labels': None,
+ 'labels': {"docker-registry": "default", "another-label": "val"},
'ports': ['5000'],
'replicas': 1,
'selector': 'type=infra',
@@ -255,6 +255,7 @@ class RegistryTest(unittest.TestCase):
mock.call(['oc', 'get', 'dc', 'docker-registry', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'get', 'svc', 'docker-registry', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'adm', 'registry',
+ "--labels=another-label=val,docker-registry=default",
'--ports=5000', '--replicas=1', '--selector=type=infra',
'--service-account=registry', '--dry-run=True', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'create', '-f', mock.ANY, '-n', 'default'], None),
diff --git a/roles/lib_openshift/src/test/unit/test_oc_adm_router.py b/roles/lib_openshift/src/test/unit/test_oc_adm_router.py
index 5481ac623..dcf768e08 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_adm_router.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_adm_router.py
@@ -300,7 +300,7 @@ class RouterTest(unittest.TestCase):
'cert_file': None,
'key_file': None,
'cacert_file': None,
- 'labels': None,
+ 'labels': {"router": "router", "another-label": "val"},
'ports': ['80:80', '443:443'],
'images': None,
'latest_images': None,
@@ -363,6 +363,7 @@ class RouterTest(unittest.TestCase):
mock.call(['oc', 'get', 'secret', 'router-certs', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'get', 'clusterrolebinding', 'router-router-role', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'adm', 'router', 'router', '--expose-metrics=False', '--external-host-insecure=False',
+ "--labels=another-label=val,router=router",
'--ports=80:80,443:443', '--replicas=2', '--selector=type=infra', '--service-account=router',
'--stats-port=1936', '--dry-run=True', '-o', 'json', '-n', 'default'], None),
mock.call(['oc', 'create', '-f', mock.ANY, '-n', 'default'], None),
diff --git a/roles/lib_openshift/src/test/unit/test_oc_configmap.py b/roles/lib_openshift/src/test/unit/test_oc_configmap.py
index 318fd6167..27042c64b 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_configmap.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_configmap.py
@@ -79,7 +79,7 @@ class OCConfigMapTest(unittest.TestCase):
''' Testing a configmap create '''
params = copy.deepcopy(OCConfigMapTest.params)
params['from_file'] = {'test': '/root/file'}
- params['from_literal'] = {'foo': 'bar', 'deployment_type': 'online'}
+ params['from_literal'] = {'foo': 'bar', 'deployment_type': 'openshift-enterprise'}
configmap = '''{
"apiVersion": "v1",
@@ -100,7 +100,7 @@ class OCConfigMapTest(unittest.TestCase):
"apiVersion": "v1",
"data": {
"foo": "bar",
- "deployment_type": "online",
+ "deployment_type": "openshift-enterprise",
"test": "this is a file\\n"
},
"kind": "ConfigMap",
@@ -128,7 +128,7 @@ class OCConfigMapTest(unittest.TestCase):
self.assertTrue(results['changed'])
self.assertEqual(results['results']['results'][0]['metadata']['name'], 'configmap')
- self.assertEqual(results['results']['results'][0]['data']['deployment_type'], 'online')
+ self.assertEqual(results['results']['results'][0]['data']['deployment_type'], 'openshift-enterprise')
@unittest.skipIf(six.PY3, 'py2 test only')
@mock.patch('os.path.exists')
diff --git a/roles/lib_openshift/src/test/unit/test_oc_pvc.py b/roles/lib_openshift/src/test/unit/test_oc_pvc.py
index 82187917d..a96f2e4a7 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_pvc.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_pvc.py
@@ -30,6 +30,8 @@ class OCPVCTest(unittest.TestCase):
'name': 'mypvc',
'namespace': 'test',
'volume_capacity': '1G',
+ 'selector': {'foo': 'bar', 'abc': 'a123'},
+ 'storage_class_name': 'mystorage',
'access_modes': 'ReadWriteMany'}
@mock.patch('oc_pvc.Utils.create_tmpfile_copy')
@@ -65,6 +67,13 @@ class OCPVCTest(unittest.TestCase):
"storage": "1Gi"
}
},
+ "selector": {
+ "matchLabels": {
+ "foo": "bar",
+ "abc": "a123"
+ }
+ },
+ "storageClassName": "myStorage",
"volumeName": "pv-aws-ow5vl"
},
"status": {
@@ -93,6 +102,8 @@ class OCPVCTest(unittest.TestCase):
self.assertTrue(results['changed'])
self.assertEqual(results['results']['results'][0]['metadata']['name'], 'mypvc')
+ self.assertEqual(results['results']['results'][0]['spec']['storageClassName'], 'myStorage')
+ self.assertEqual(results['results']['results'][0]['spec']['selector']['matchLabels']['foo'], 'bar')
@mock.patch('oc_pvc.Utils.create_tmpfile_copy')
@mock.patch('oc_pvc.OCPVC._run')
diff --git a/roles/lib_openshift/src/test/unit/test_oc_route.py b/roles/lib_openshift/src/test/unit/test_oc_route.py
index afdb5e4dc..5699f123b 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_route.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_route.py
@@ -39,6 +39,7 @@ class OCRouteTest(unittest.TestCase):
'debug': False,
'name': 'test',
'namespace': 'default',
+ 'labels': {'route': 'route'},
'tls_termination': 'passthrough',
'dest_cacert_path': None,
'cacert_path': None,
@@ -64,7 +65,10 @@ class OCRouteTest(unittest.TestCase):
"selfLink": "/oapi/v1/namespaces/default/routes/test",
"uid": "1b127c67-ecd9-11e6-96eb-0e0d9bdacd26",
"resourceVersion": "439182",
- "creationTimestamp": "2017-02-07T01:59:48Z"
+ "creationTimestamp": "2017-02-07T01:59:48Z",
+ "labels": {
+ "route": "route"
+ }
},
"spec": {
"host": "test.example",
@@ -141,6 +145,7 @@ class OCRouteTest(unittest.TestCase):
'debug': False,
'name': 'test',
'namespace': 'default',
+ 'labels': {'route': 'route'},
'tls_termination': 'edge',
'dest_cacert_path': None,
'cacert_path': None,
@@ -166,7 +171,8 @@ class OCRouteTest(unittest.TestCase):
"namespace": "default",
"resourceVersion": "517745",
"selfLink": "/oapi/v1/namespaces/default/routes/test",
- "uid": "b6f25898-ed77-11e6-9755-0e737db1e63a"
+ "uid": "b6f25898-ed77-11e6-9755-0e737db1e63a",
+ "labels": {"route": "route"}
},
"spec": {
"host": "test.openshift.com",
@@ -250,6 +256,7 @@ metadata:
self.assertTrue(results['changed'])
self.assertEqual(results['state'], 'present')
self.assertEqual(results['results']['results'][0]['metadata']['name'], 'test')
+ self.assertEqual(results['results']['results'][0]['metadata']['labels']['route'], 'route')
# Making sure our mock was called as we expected
mock_cmd.assert_has_calls([
diff --git a/roles/lib_openshift/src/test/unit/test_oc_secret.py b/roles/lib_openshift/src/test/unit/test_oc_secret.py
index e31393793..323b3423c 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_secret.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_secret.py
@@ -38,6 +38,7 @@ class OCSecretTest(unittest.TestCase):
'state': 'present',
'namespace': 'default',
'name': 'testsecretname',
+ 'type': 'Opaque',
'contents': [{
'path': "/tmp/somesecret.json",
'data': "{'one': 1, 'two': 2, 'three': 3}",
@@ -47,6 +48,7 @@ class OCSecretTest(unittest.TestCase):
'debug': False,
'files': None,
'delete_after': True,
+ 'force': False,
}
# Return values of our mocked function call. These get returned once per call.
@@ -74,7 +76,7 @@ class OCSecretTest(unittest.TestCase):
# Making sure our mock was called as we expected
mock_cmd.assert_has_calls([
mock.call(['oc', 'get', 'secrets', 'testsecretname', '-o', 'json', '-n', 'default'], None),
- mock.call(['oc', 'secrets', 'new', 'testsecretname', mock.ANY, '-n', 'default'], None),
+ mock.call(['oc', 'secrets', 'new', 'testsecretname', '--type=Opaque', mock.ANY, '-n', 'default'], None),
])
mock_write.assert_has_calls([
diff --git a/roles/lib_openshift/src/test/unit/test_oc_service.py b/roles/lib_openshift/src/test/unit/test_oc_service.py
index e74c66665..9c21a262f 100755
--- a/roles/lib_openshift/src/test/unit/test_oc_service.py
+++ b/roles/lib_openshift/src/test/unit/test_oc_service.py
@@ -39,6 +39,7 @@ class OCServiceTest(unittest.TestCase):
'selector': None,
'session_affinity': None,
'service_type': None,
+ 'external_ips': None,
'kubeconfig': '/etc/origin/master/admin.kubeconfig',
'debug': False}
@@ -124,6 +125,7 @@ class OCServiceTest(unittest.TestCase):
'selector': {'router': 'router'},
'session_affinity': 'ClientIP',
'service_type': 'ClusterIP',
+ 'external_ips': None,
'kubeconfig': '/etc/origin/master/admin.kubeconfig',
'debug': False}
@@ -303,3 +305,183 @@ class OCServiceTest(unittest.TestCase):
mock_shutil_which.side_effect = lambda _f, path=None: oc_bin
self.assertEqual(locate_oc_binary(), oc_bin)
+
+ @mock.patch('oc_service.Utils.create_tmpfile_copy')
+ @mock.patch('oc_service.OCService._run')
+ def test_create_with_labels(self, mock_cmd, mock_tmpfile_copy):
+ ''' Testing a create service '''
+ params = {'name': 'router',
+ 'namespace': 'default',
+ 'ports': {'name': '9000-tcp',
+ 'port': 9000,
+ 'protocol': 'TCP',
+ 'targetPOrt': 9000},
+ 'state': 'present',
+ 'labels': {'component': 'some_component', 'infra': 'true'},
+ 'clusterip': None,
+ 'portalip': None,
+ 'selector': {'router': 'router'},
+ 'session_affinity': 'ClientIP',
+ 'service_type': 'ClusterIP',
+ 'external_ips': None,
+ 'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+ 'debug': False}
+
+ service = '''{
+ "kind": "Service",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "router",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/services/router",
+ "uid": "fabd2440-e3d8-11e6-951c-0e3dd518cefa",
+ "resourceVersion": "3206",
+ "creationTimestamp": "2017-01-26T15:06:14Z",
+ "labels": {"component": "some_component", "infra": "true"}
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "80-tcp",
+ "protocol": "TCP",
+ "port": 80,
+ "targetPort": 80
+ },
+ {
+ "name": "443-tcp",
+ "protocol": "TCP",
+ "port": 443,
+ "targetPort": 443
+ },
+ {
+ "name": "1936-tcp",
+ "protocol": "TCP",
+ "port": 1936,
+ "targetPort": 1936
+ },
+ {
+ "name": "5000-tcp",
+ "protocol": "TCP",
+ "port": 5000,
+ "targetPort": 5000
+ }
+ ],
+ "selector": {
+ "router": "router"
+ },
+ "clusterIP": "172.30.129.161",
+ "type": "ClusterIP",
+ "sessionAffinity": "None"
+ },
+ "status": {
+ "loadBalancer": {}
+ }
+ }'''
+ mock_cmd.side_effect = [
+ (1, '', 'Error from server: services "router" not found'),
+ (1, '', 'Error from server: services "router" not found'),
+ (0, service, ''),
+ (0, service, '')
+ ]
+
+ mock_tmpfile_copy.side_effect = [
+ '/tmp/mocked_kubeconfig',
+ ]
+
+ results = OCService.run_ansible(params, False)
+
+ self.assertTrue(results['changed'])
+ self.assertTrue(results['results']['returncode'] == 0)
+ self.assertEqual(results['results']['results'][0]['metadata']['name'], 'router')
+ self.assertEqual(results['results']['results'][0]['metadata']['labels'], {"component": "some_component", "infra": "true"})
+
+ @mock.patch('oc_service.Utils.create_tmpfile_copy')
+ @mock.patch('oc_service.OCService._run')
+ def test_create_with_external_ips(self, mock_cmd, mock_tmpfile_copy):
+ ''' Testing a create service '''
+ params = {'name': 'router',
+ 'namespace': 'default',
+ 'ports': {'name': '9000-tcp',
+ 'port': 9000,
+ 'protocol': 'TCP',
+ 'targetPOrt': 9000},
+ 'state': 'present',
+ 'labels': {'component': 'some_component', 'infra': 'true'},
+ 'clusterip': None,
+ 'portalip': None,
+ 'selector': {'router': 'router'},
+ 'session_affinity': 'ClientIP',
+ 'service_type': 'ClusterIP',
+ 'external_ips': ['1.2.3.4', '5.6.7.8'],
+ 'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+ 'debug': False}
+
+ service = '''{
+ "kind": "Service",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "router",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/services/router",
+ "uid": "fabd2440-e3d8-11e6-951c-0e3dd518cefa",
+ "resourceVersion": "3206",
+ "creationTimestamp": "2017-01-26T15:06:14Z",
+ "labels": {"component": "some_component", "infra": "true"}
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "80-tcp",
+ "protocol": "TCP",
+ "port": 80,
+ "targetPort": 80
+ },
+ {
+ "name": "443-tcp",
+ "protocol": "TCP",
+ "port": 443,
+ "targetPort": 443
+ },
+ {
+ "name": "1936-tcp",
+ "protocol": "TCP",
+ "port": 1936,
+ "targetPort": 1936
+ },
+ {
+ "name": "5000-tcp",
+ "protocol": "TCP",
+ "port": 5000,
+ "targetPort": 5000
+ }
+ ],
+ "selector": {
+ "router": "router"
+ },
+ "clusterIP": "172.30.129.161",
+ "externalIPs": ["1.2.3.4", "5.6.7.8"],
+ "type": "ClusterIP",
+ "sessionAffinity": "None"
+ },
+ "status": {
+ "loadBalancer": {}
+ }
+ }'''
+ mock_cmd.side_effect = [
+ (1, '', 'Error from server: services "router" not found'),
+ (1, '', 'Error from server: services "router" not found'),
+ (0, service, ''),
+ (0, service, '')
+ ]
+
+ mock_tmpfile_copy.side_effect = [
+ '/tmp/mocked_kubeconfig',
+ ]
+
+ results = OCService.run_ansible(params, False)
+
+ self.assertTrue(results['changed'])
+ self.assertTrue(results['results']['returncode'] == 0)
+ self.assertEqual(results['results']['results'][0]['metadata']['name'], 'router')
+ self.assertEqual(results['results']['results'][0]['metadata']['labels'], {"component": "some_component", "infra": "true"})
+ self.assertEqual(results['results']['results'][0]['spec']['externalIPs'], ["1.2.3.4", "5.6.7.8"])
diff --git a/roles/lib_openshift/src/test/unit/test_oc_storageclass.py b/roles/lib_openshift/src/test/unit/test_oc_storageclass.py
new file mode 100755
index 000000000..4fd02a8b1
--- /dev/null
+++ b/roles/lib_openshift/src/test/unit/test_oc_storageclass.py
@@ -0,0 +1,93 @@
+'''
+ Unit tests for oc serviceaccount
+'''
+
+import os
+import sys
+import unittest
+import mock
+
+# Removing invalid variable names for tests so that I can
+# keep them brief
+# pylint: disable=invalid-name,no-name-in-module
+# Disable import-error b/c our libraries aren't loaded in jenkins
+# pylint: disable=import-error
+# place class in our python path
+module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501
+sys.path.insert(0, module_path)
+from oc_storageclass import OCStorageClass # noqa: E402
+
+
+class OCStorageClassTest(unittest.TestCase):
+ '''
+ Test class for OCStorageClass
+ '''
+ params = {'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+ 'state': 'present',
+ 'debug': False,
+ 'name': 'testsc',
+ 'provisioner': 'kubernetes.io/aws-ebs',
+ 'annotations': {'storageclass.beta.kubernetes.io/is-default-class': "true"},
+ 'parameters': {'type': 'gp2'},
+ 'api_version': 'v1',
+ 'default_storage_class': 'true'}
+
+ @mock.patch('oc_storageclass.locate_oc_binary')
+ @mock.patch('oc_storageclass.Utils.create_tmpfile_copy')
+ @mock.patch('oc_storageclass.OCStorageClass._run')
+ def test_adding_a_storageclass(self, mock_cmd, mock_tmpfile_copy, mock_oc_binary):
+ ''' Testing adding a storageclass '''
+
+ # Arrange
+
+ # run_ansible input parameters
+
+ valid_result_json = '''{
+ "kind": "StorageClass",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "testsc",
+ "selfLink": "/apis/storage.k8s.io/v1/storageclasses/gp2",
+ "uid": "4d8320c9-e66f-11e6-8edc-0eece8f2ce22",
+ "resourceVersion": "2828",
+ "creationTimestamp": "2017-01-29T22:07:19Z",
+ "annotations": {"storageclass.beta.kubernetes.io/is-default-class": "true"}
+ },
+ "provisioner": "kubernetes.io/aws-ebs",
+ "parameters": {"type": "gp2"}
+ }'''
+
+ # Return values of our mocked function call. These get returned once per call.
+ mock_cmd.side_effect = [
+ # First call to mock
+ (1, '', 'Error from server: storageclass "testsc" not found'),
+
+ # Second call to mock
+ (0, 'storageclass "testsc" created', ''),
+
+ # Third call to mock
+ (0, valid_result_json, ''),
+ ]
+
+ mock_oc_binary.side_effect = [
+ 'oc'
+ ]
+
+ mock_tmpfile_copy.side_effect = [
+ '/tmp/mocked_kubeconfig',
+ ]
+
+ # Act
+ results = OCStorageClass.run_ansible(OCStorageClassTest.params, False)
+
+ # Assert
+ self.assertTrue(results['changed'])
+ self.assertEqual(results['results']['returncode'], 0)
+ self.assertEqual(results['state'], 'present')
+
+ # Making sure our mock was called as we expected
+ mock_cmd.assert_has_calls([
+ mock.call(['oc', 'get', 'storageclass', 'testsc', '-o', 'json'], None),
+ mock.call(['oc', 'create', '-f', mock.ANY], None),
+ mock.call(['oc', 'get', 'storageclass', 'testsc', '-o', 'json'], None),
+ ])