summaryrefslogtreecommitdiffstats
path: root/roles/lib_openshift/src
diff options
context:
space:
mode:
authorKenny Woodson <kwoodson@redhat.com>2017-02-07 21:44:47 -0500
committerKenny Woodson <kwoodson@redhat.com>2017-02-08 14:03:59 -0500
commitd338c1aefc702919e6fac553c60a69455ae37d05 (patch)
tree680e7d55dfc49cacdf3f7b0adf040943bbf7b8c9 /roles/lib_openshift/src
parent5c6dc36dda1e2ef87f4e43c77e682d829a6bbd3c (diff)
downloadopenshift-d338c1aefc702919e6fac553c60a69455ae37d05.tar.gz
openshift-d338c1aefc702919e6fac553c60a69455ae37d05.tar.bz2
openshift-d338c1aefc702919e6fac553c60a69455ae37d05.tar.xz
openshift-d338c1aefc702919e6fac553c60a69455ae37d05.zip
Adding oc_env to lib_openshift.
Diffstat (limited to 'roles/lib_openshift/src')
-rw-r--r--roles/lib_openshift/src/ansible/oc_env.py32
-rw-r--r--roles/lib_openshift/src/class/oc_env.py144
-rw-r--r--roles/lib_openshift/src/doc/env76
-rw-r--r--roles/lib_openshift/src/sources.yml11
-rwxr-xr-xroles/lib_openshift/src/test/unit/oc_env.py430
5 files changed, 693 insertions, 0 deletions
diff --git a/roles/lib_openshift/src/ansible/oc_env.py b/roles/lib_openshift/src/ansible/oc_env.py
new file mode 100644
index 000000000..4a58f7ec1
--- /dev/null
+++ b/roles/lib_openshift/src/ansible/oc_env.py
@@ -0,0 +1,32 @@
+# pylint: skip-file
+
+def main():
+ '''
+ ansible oc module for environment variables
+ '''
+
+ module = AnsibleModule(
+ argument_spec=dict(
+ kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
+ state=dict(default='present', type='str',
+ choices=['present', 'absent', 'list']),
+ debug=dict(default=False, type='bool'),
+ kind=dict(default='rc', choices=['dc', 'rc', 'pods'], type='str'),
+ namespace=dict(default='default', type='str'),
+ name=dict(default=None, required=True, type='str'),
+ env_vars=dict(default=None, type='dict'),
+ ),
+ mutually_exclusive=[["content", "files"]],
+
+ supports_check_mode=True,
+ )
+ results = OCEnv.run_ansible(module.params, module.check_mode)
+
+ if 'failed' in results:
+ module.fail_json(**results)
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/roles/lib_openshift/src/class/oc_env.py b/roles/lib_openshift/src/class/oc_env.py
new file mode 100644
index 000000000..a7ea0c9eb
--- /dev/null
+++ b/roles/lib_openshift/src/class/oc_env.py
@@ -0,0 +1,144 @@
+# pylint: skip-file
+
+# pylint: disable=too-many-instance-attributes
+class OCEnv(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools '''
+
+ container_path = {"pod": "spec.containers[0].env",
+ "dc": "spec.template.spec.containers[0].env",
+ "rc": "spec.template.spec.containers[0].env",
+ }
+
+ # pylint allows 5. we need 6
+ # pylint: disable=too-many-arguments
+ def __init__(self,
+ namespace,
+ kind,
+ env_vars,
+ resource_name=None,
+ kubeconfig='/etc/origin/master/admin.kubeconfig',
+ verbose=False):
+ ''' Constructor for OpenshiftOC '''
+ super(OCEnv, self).__init__(namespace, kubeconfig)
+ self.kind = kind
+ self.name = resource_name
+ self.namespace = namespace
+ self.env_vars = env_vars
+ self.kubeconfig = kubeconfig
+ self.verbose = verbose
+ self._resource = None
+
+ @property
+ def resource(self):
+ ''' property function for resource var'''
+ if not self._resource:
+ self.get()
+ return self._resource
+
+ @resource.setter
+ def resource(self, data):
+ ''' setter function for resource var'''
+ self._resource = data
+
+ def value_exists(self, key, value):
+ ''' return whether a key, value pair exists '''
+ return self.resource.exists_env_value(key, value)
+
+ def key_exists(self, key):
+ ''' return whether a key, value pair exists '''
+ return self.resource.exists_env_key(key)
+
+ def get(self):
+ '''return a environment variables '''
+ result = self._get(self.kind, self.name)
+ if result['returncode'] == 0:
+ if self.kind == 'dc':
+ self.resource = DeploymentConfig(content=result['results'][0])
+ result['results'] = self.resource.get(OCEnv.container_path[self.kind]) or []
+ return result
+
+ def delete(self):
+ '''return all pods '''
+ #yed.put(OCEnv.container_path[self.kind], env_vars_array)
+ if self.resource.delete_env_var(self.env_vars.keys()):
+ return self._replace_content(self.kind, self.name, self.resource.yaml_dict)
+
+ return {'returncode': 0, 'changed': False}
+
+ # pylint: disable=too-many-function-args
+ def put(self):
+ '''place env vars into dc '''
+ for update_key, update_value in self.env_vars.items():
+ self.resource.update_env_var(update_key, update_value)
+
+ return self._replace_content(self.kind, self.name, self.resource.yaml_dict)
+
+ @staticmethod
+ def run_ansible(params, check_mode):
+ '''run the idempotent ansible code'''
+
+ ocenv = OCEnv(params['namespace'],
+ params['kind'],
+ params['env_vars'],
+ resource_name=params['name'],
+ kubeconfig=params['kubeconfig'],
+ verbose=params['debug'])
+
+ state = params['state']
+
+ api_rval = ocenv.get()
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ return {'changed': False, 'results': api_rval['results'], 'state': "list"}
+
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ for key in params.get('env_vars', {}).keys():
+ if ocenv.resource.exists_env_key(key):
+
+ if check_mode:
+ return {'changed': False,
+ 'msg': 'CHECK_MODE: Would have performed a delete.'}
+
+ api_rval = ocenv.delete()
+
+ return {'changed': True, 'results': results, 'state': 'absent'}
+
+ return {'changed': False, 'state': 'absent'}
+
+ if state == 'present':
+ ########
+ # Create
+ ########
+ for key, value in params.get('env_vars', {}).items():
+ if not ocenv.value_exists(key, value):
+
+ if check_mode:
+ return {'changed': False,
+ 'msg': 'CHECK_MODE: Would have performed a create.'}
+
+ # Create it here
+ api_rval = ocenv.put()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ # return the created object
+ api_rval = ocenv.get()
+
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ return {'changed': True, 'results': results, 'state': 'present'}
+
+ return {'changed': False, 'results': results, 'state': 'present'}
+
+
+ return {'failed': True,
+ 'changed': False,
+ 'msg': 'Unknown state passed. %s' % state}
diff --git a/roles/lib_openshift/src/doc/env b/roles/lib_openshift/src/doc/env
new file mode 100644
index 000000000..0544014e6
--- /dev/null
+++ b/roles/lib_openshift/src/doc/env
@@ -0,0 +1,76 @@
+# flake8: noqa
+# pylint: skip-file
+
+DOCUMENTATION = '''
+---
+module: oc_env
+short_description: Modify, and idempotently manage openshift environment variables on pods, deploymentconfigs, and replication controllers.
+description:
+ - Modify openshift environment variables programmatically.
+options:
+ state:
+ description:
+ - Supported states, present, absent, list
+ - present - will ensure object is created or updated to the value specified
+ - list - will return a list of environment variables
+ - absent - will remove the environment varibale from the object
+ required: False
+ default: present
+ choices: ["present", 'absent', 'list']
+ aliases: []
+ kubeconfig:
+ description:
+ - The path for the kubeconfig file to use for authentication
+ required: false
+ default: /etc/origin/master/admin.kubeconfig
+ aliases: []
+ debug:
+ description:
+ - Turn on debug output.
+ required: false
+ default: False
+ aliases: []
+ name:
+ description:
+ - Name of the object that is being queried.
+ required: false
+ default: None
+ aliases: []
+ namespace:
+ description:
+ - The namespace where the object lives.
+ required: false
+ default: str
+ aliases: []
+ kind:
+ description:
+ - The kind attribute of the object.
+ required: False
+ default: dc
+ choices:
+ - rc
+ - dc
+ - pods
+ aliases: []
+author:
+- "Kenny Woodson <kwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+- name: query a list of env vars on dc
+ oc_env:
+ kind: dc
+ name: myawesomedc
+ namespace: phpapp
+
+- name: Set the following variables.
+ oc_env:
+ kind: dc
+ name: myawesomedc
+ namespace: phpapp
+ env_vars:
+ SUPER_TURBO_MODE: 'true'
+ ENABLE_PORTS: 'false'
+ SERVICE_PORT: 9999
+'''
diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml
index aa02ce120..badb1b1a4 100644
--- a/roles/lib_openshift/src/sources.yml
+++ b/roles/lib_openshift/src/sources.yml
@@ -19,6 +19,17 @@ oc_edit.py:
- class/oc_edit.py
- ansible/oc_edit.py
+oc_env.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/env
+- ../../lib_utils/src/class/yedit.py
+- lib/base.py
+- lib/deploymentconfig.py
+- class/oc_env.py
+- ansible/oc_env.py
+
oc_label.py:
- doc/generated
- doc/license
diff --git a/roles/lib_openshift/src/test/unit/oc_env.py b/roles/lib_openshift/src/test/unit/oc_env.py
new file mode 100755
index 000000000..5661065b9
--- /dev/null
+++ b/roles/lib_openshift/src/test/unit/oc_env.py
@@ -0,0 +1,430 @@
+#!/usr/bin/env python2
+'''
+ Unit tests for oc_env
+'''
+# To run:
+# ./oc_env.py
+#
+# .
+# Ran 1 test in 0.002s
+#
+# OK
+
+import os
+import sys
+import unittest
+import mock
+
+# Removing invalid variable names for tests so that I can
+# keep them brief
+# pylint: disable=invalid-name,no-name-in-module
+# Disable import-error b/c our libraries aren't loaded in jenkins
+# pylint: disable=import-error,wrong-import-position
+# place class in our python path
+module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501
+sys.path.insert(0, module_path)
+from oc_env import OCEnv # noqa: E402
+
+
+class OCEnvTest(unittest.TestCase):
+ '''
+ Test class for OCEnv
+ '''
+
+ def setUp(self):
+ ''' setup method will create a file and set to known configuration '''
+ pass
+
+ @mock.patch('oc_env.OCEnv._run')
+ def test_listing_all_env_vars(self, mock_cmd):
+ ''' Testing listing all environment variables from a dc'''
+
+ # Arrange
+
+ # run_ansible input parameters
+ params = {
+ 'state': 'list',
+ 'namespace': 'default',
+ 'name': 'router',
+ 'kind': 'dc',
+ 'list_all': False,
+ 'env_vars': None,
+ 'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+ 'debug': False,
+ }
+
+ dc_results = '''{
+ "apiVersion": "v1",
+ "kind": "DeploymentConfig",
+ "metadata": {
+ "creationTimestamp": "2017-02-02T15:58:49Z",
+ "generation": 8,
+ "labels": {
+ "router": "router"
+ },
+ "name": "router",
+ "namespace": "default",
+ "resourceVersion": "513678",
+ "selfLink": "/oapi/v1/namespaces/default/deploymentconfigs/router",
+ "uid": "7c705902-e960-11e6-b041-0ed9df7abc38"
+ },
+ "spec": {
+ "replicas": 2,
+ "selector": {
+ "router": "router"
+ },
+ "strategy": {
+ "activeDeadlineSeconds": 21600,
+ "resources": {},
+ "rollingParams": {
+ "intervalSeconds": 1,
+ "maxSurge": "50%",
+ "maxUnavailable": "50%",
+ "timeoutSeconds": 600,
+ "updatePeriodSeconds": 1
+ },
+ "type": "Rolling"
+ },
+ "template": {
+ "metadata": {
+ "creationTimestamp": null,
+ "labels": {
+ "router": "router"
+ }
+ },
+ "spec": {
+ "containers": [
+ {
+ "env": [
+ {
+ "name": "DEFAULT_CERTIFICATE_DIR",
+ "value": "/etc/pki/tls/private"
+ },
+ {
+ "name": "DEFAULT_CERTIFICATE_PATH",
+ "value": "/etc/pki/tls/private/tls.crt"
+ },
+ {
+ "name": "ROUTER_EXTERNAL_HOST_HOSTNAME"
+ },
+ {
+ "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER"
+ },
+ {
+ "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER"
+ },
+ {
+ "name": "ROUTER_EXTERNAL_HOST_INSECURE",
+ "value": "false"
+ },
+ {
+ "name": "ROUTER_EXTERNAL_HOST_INTERNAL_ADDRESS"
+ },
+ {
+ "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_EXTERNAL_HOST_VXLAN_GW_CIDR"
+ },
+ {
+ "name": "ROUTER_SERVICE_HTTPS_PORT",
+ "value": "443"
+ },
+ {
+ "name": "ROUTER_SERVICE_HTTP_PORT",
+ "value": "80"
+ },
+ {
+ "name": "ROUTER_SERVICE_NAME",
+ "value": "router"
+ },
+ {
+ "name": "ROUTER_SERVICE_NAMESPACE",
+ "value": "default"
+ },
+ {
+ "name": "ROUTER_SUBDOMAIN"
+ },
+ {
+ "name": "STATS_PASSWORD",
+ "value": "UEKR5GCWGI"
+ },
+ {
+ "name": "STATS_PORT",
+ "value": "1936"
+ },
+ {
+ "name": "STATS_USERNAME",
+ "value": "admin"
+ },
+ {
+ "name": "EXTENDED_VALIDATION",
+ "value": "false"
+ },
+ {
+ "name": "ROUTER_USE_PROXY_PROTOCOL",
+ "value": "true"
+ }
+ ],
+ "image": "openshift3/ose-haproxy-router:v3.5.0.17",
+ "imagePullPolicy": "IfNotPresent",
+ "livenessProbe": {
+ "failureThreshold": 3,
+ "httpGet": {
+ "host": "localhost",
+ "path": "/healthz",
+ "port": 1936,
+ "scheme": "HTTP"
+ },
+ "initialDelaySeconds": 10,
+ "periodSeconds": 10,
+ "successThreshold": 1,
+ "timeoutSeconds": 1
+ },
+ "name": "router",
+ "ports": [
+ {
+ "containerPort": 80,
+ "hostPort": 80,
+ "protocol": "TCP"
+ },
+ {
+ "containerPort": 443,
+ "hostPort": 443,
+ "protocol": "TCP"
+ },
+ {
+ "containerPort": 5000,
+ "hostPort": 5000,
+ "protocol": "TCP"
+ },
+ {
+ "containerPort": 1936,
+ "hostPort": 1936,
+ "name": "stats",
+ "protocol": "TCP"
+ }
+ ],
+ "readinessProbe": {
+ "failureThreshold": 3,
+ "httpGet": {
+ "host": "localhost",
+ "path": "/healthz",
+ "port": 1936,
+ "scheme": "HTTP"
+ },
+ "initialDelaySeconds": 10,
+ "periodSeconds": 10,
+ "successThreshold": 1,
+ "timeoutSeconds": 1
+ },
+ "resources": {
+ "requests": {
+ "cpu": "100m",
+ "memory": "256Mi"
+ }
+ },
+ "terminationMessagePath": "/dev/termination-log",
+ "volumeMounts": [
+ {
+ "mountPath": "/etc/pki/tls/private",
+ "name": "server-certificate",
+ "readOnly": true
+ }
+ ]
+ }
+ ],
+ "dnsPolicy": "ClusterFirst",
+ "hostNetwork": true,
+ "nodeSelector": {
+ "type": "infra"
+ },
+ "restartPolicy": "Always",
+ "securityContext": {},
+ "serviceAccount": "router",
+ "serviceAccountName": "router",
+ "terminationGracePeriodSeconds": 30,
+ "volumes": [
+ {
+ "name": "server-certificate",
+ "secret": {
+ "defaultMode": 420,
+ "secretName": "router-certs"
+ }
+ }
+ ]
+ }
+ },
+ "test": false,
+ "triggers": [
+ {
+ "type": "ConfigChange"
+ }
+ ]
+ },
+ "status": {
+ "availableReplicas": 2,
+ "conditions": [
+ {
+ "lastTransitionTime": "2017-02-02T15:59:12Z",
+ "lastUpdateTime": null,
+ "message": "Deployment config has minimum availability.",
+ "status": "True",
+ "type": "Available"
+ },
+ {
+ "lastTransitionTime": "2017-02-07T19:55:26Z",
+ "lastUpdateTime": "2017-02-07T19:55:26Z",
+ "message": "replication controller router-2 has failed progressing",
+ "reason": "ProgressDeadlineExceeded",
+ "status": "False",
+ "type": "Progressing"
+ }
+ ],
+ "details": {
+ "causes": [
+ {
+ "type": "ConfigChange"
+ }
+ ],
+ "message": "config change"
+ },
+ "latestVersion": 2,
+ "observedGeneration": 8,
+ "readyReplicas": 2,
+ "replicas": 2,
+ "unavailableReplicas": 0,
+ "updatedReplicas": 0
+ }
+ }'''
+
+ # Return values of our mocked function call. These get returned once per call.
+ mock_cmd.side_effect = [
+ (0, dc_results, ''), # First call to the mock
+ ]
+
+ # Act
+ results = OCEnv.run_ansible(params, False)
+
+ # Assert
+ self.assertFalse(results['changed'])
+ for env_var in results['results']:
+ if env_var == {'name': 'DEFAULT_CERTIFICATE_DIR', 'value': '/etc/pki/tls/private'}:
+ break
+ else:
+ self.fail('Did not find envionrment variables in results.')
+ self.assertEqual(results['state'], 'list')
+
+ # Making sure our mocks were called as we expected
+ mock_cmd.assert_has_calls([
+ mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None),
+ ])
+
+# @mock.patch('oc_serviceaccount_secret.Yedit._write')
+# @mock.patch('oc_serviceaccount_secret.OCServiceAccountSecret._run')
+# def test_removing_a_secret_to_a_serviceaccount(self, mock_cmd, mock_write):
+# ''' Testing adding a secret to a service account '''
+#
+# # Arrange
+#
+# # run_ansible input parameters
+# params = {
+# 'state': 'absent',
+# 'namespace': 'default',
+# 'secret': 'newsecret',
+# 'service_account': 'builder',
+# 'kubeconfig': '/etc/origin/master/admin.kubeconfig',
+# 'debug': False,
+# }
+#
+# oc_get_sa_before = '''{
+# "kind": "ServiceAccount",
+# "apiVersion": "v1",
+# "metadata": {
+# "name": "builder",
+# "namespace": "default",
+# "selfLink": "/api/v1/namespaces/default/serviceaccounts/builder",
+# "uid": "cf47bca7-ebc4-11e6-b041-0ed9df7abc38",
+# "resourceVersion": "302879",
+# "creationTimestamp": "2017-02-05T17:02:00Z"
+# },
+# "secrets": [
+# {
+# "name": "builder-dockercfg-rsrua"
+# },
+# {
+# "name": "builder-token-akqxi"
+# },
+# {
+# "name": "newsecret"
+# }
+#
+# ],
+# "imagePullSecrets": [
+# {
+# "name": "builder-dockercfg-rsrua"
+# }
+# ]
+# }
+# '''
+#
+# builder_yaml_file = '''\
+#secrets:
+#- name: builder-dockercfg-rsrua
+#- name: builder-token-akqxi
+#kind: ServiceAccount
+#imagePullSecrets:
+#- name: builder-dockercfg-rsrua
+#apiVersion: v1
+#metadata:
+# name: builder
+# namespace: default
+# resourceVersion: '302879'
+# creationTimestamp: '2017-02-05T17:02:00Z'
+# selfLink: /api/v1/namespaces/default/serviceaccounts/builder
+# uid: cf47bca7-ebc4-11e6-b041-0ed9df7abc38
+#'''
+#
+# # Return values of our mocked function call. These get returned once per call.
+# mock_cmd.side_effect = [
+# (0, oc_get_sa_before, ''), # First call to the mock
+# (0, oc_get_sa_before, ''), # Second call to the mock
+# (0, 'serviceaccount "builder" replaced', ''), # Third call to the mock
+# ]
+#
+# # Act
+# results = OCServiceAccountSecret.run_ansible(params, False)
+#
+# # Assert
+# self.assertTrue(results['changed'])
+# self.assertEqual(results['results']['returncode'], 0)
+# self.assertEqual(results['state'], 'absent')
+#
+# # Making sure our mocks were called as we expected
+# mock_cmd.assert_has_calls([
+# mock.call(['oc', '-n', 'default', 'get', 'sa', 'builder', '-o', 'json'], None),
+# mock.call(['oc', '-n', 'default', 'get', 'sa', 'builder', '-o', 'json'], None),
+# mock.call(['oc', '-n', 'default', 'replace', '-f', '/tmp/builder'], None),
+# ])
+#
+# mock_write.assert_has_calls([
+# mock.call('/tmp/builder', builder_yaml_file)
+# ])
+
+ def tearDown(self):
+ '''TearDown method'''
+ pass
+
+
+if __name__ == "__main__":
+ unittest.main()