Browse Source

Adding oc_env to lib_openshift.

Kenny Woodson 8 years ago
parent
commit
d338c1aefc

File diff suppressed because it is too large
+ 1784 - 0
roles/lib_openshift/library/oc_env.py


+ 32 - 0
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()

+ 144 - 0
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}

+ 76 - 0
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
+'''

+ 11 - 0
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

+ 430 - 0
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()