summaryrefslogtreecommitdiffstats
path: root/roles/lib_openshift/src
diff options
context:
space:
mode:
authorThomas Wiest <twiest@redhat.com>2017-01-29 14:47:07 -0500
committerThomas Wiest <twiest@redhat.com>2017-02-06 17:03:48 -0500
commitd508ec24877a743c6d79dac0574c859e14d40218 (patch)
treeba462f616ca507e33cfbf4dc18e3764eb1aad4a1 /roles/lib_openshift/src
parent76d0a4538baa3b59085d6dd57b92ffd145c76f93 (diff)
downloadopenshift-d508ec24877a743c6d79dac0574c859e14d40218.tar.gz
openshift-d508ec24877a743c6d79dac0574c859e14d40218.tar.bz2
openshift-d508ec24877a743c6d79dac0574c859e14d40218.tar.xz
openshift-d508ec24877a743c6d79dac0574c859e14d40218.zip
Added oc_serviceaccount_secret to lib_openshift.
Diffstat (limited to 'roles/lib_openshift/src')
-rw-r--r--roles/lib_openshift/src/ansible/oc_serviceaccount.py2
-rw-r--r--roles/lib_openshift/src/ansible/oc_serviceaccount_secret.py29
-rw-r--r--roles/lib_openshift/src/class/oc_serviceaccount_secret.py138
-rw-r--r--roles/lib_openshift/src/doc/serviceaccount_secret68
-rw-r--r--roles/lib_openshift/src/sources.yml11
-rwxr-xr-xroles/lib_openshift/src/test/integration/oc_serviceaccount_secret.yml79
-rwxr-xr-xroles/lib_openshift/src/test/unit/oc_serviceaccount.py6
-rwxr-xr-xroles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py257
8 files changed, 586 insertions, 4 deletions
diff --git a/roles/lib_openshift/src/ansible/oc_serviceaccount.py b/roles/lib_openshift/src/ansible/oc_serviceaccount.py
index ea9bdb455..bc9332f9a 100644
--- a/roles/lib_openshift/src/ansible/oc_serviceaccount.py
+++ b/roles/lib_openshift/src/ansible/oc_serviceaccount.py
@@ -3,7 +3,7 @@
def main():
'''
- ansible oc module for route
+ ansible oc module for service accounts
'''
module = AnsibleModule(
diff --git a/roles/lib_openshift/src/ansible/oc_serviceaccount_secret.py b/roles/lib_openshift/src/ansible/oc_serviceaccount_secret.py
new file mode 100644
index 000000000..554d9a6a2
--- /dev/null
+++ b/roles/lib_openshift/src/ansible/oc_serviceaccount_secret.py
@@ -0,0 +1,29 @@
+# pylint: skip-file
+# flake8: noqa
+
+def main():
+ '''
+ ansible oc module to manage service account secrets.
+ '''
+
+ module = AnsibleModule(
+ argument_spec=dict(
+ kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
+ state=dict(default='present', type='str',
+ choices=['present', 'absent', 'list']),
+ debug=dict(default=False, type='bool'),
+ namespace=dict(default=None, required=True, type='str'),
+ secret=dict(default=None, type='str'),
+ service_account=dict(required=True, type='str'),
+ ),
+ supports_check_mode=True,
+ )
+
+ rval = OCServiceAccountSecret.run_ansible(module.params, module.check_mode)
+ if 'failed' in rval:
+ module.fail_json(**rval)
+
+ module.exit_json(**rval)
+
+if __name__ == '__main__':
+ main()
diff --git a/roles/lib_openshift/src/class/oc_serviceaccount_secret.py b/roles/lib_openshift/src/class/oc_serviceaccount_secret.py
new file mode 100644
index 000000000..750a74d33
--- /dev/null
+++ b/roles/lib_openshift/src/class/oc_serviceaccount_secret.py
@@ -0,0 +1,138 @@
+# pylint: skip-file
+# flake8: noqa
+
+class OCServiceAccountSecret(OpenShiftCLI):
+ ''' Class to wrap the oc command line tools '''
+
+ kind = 'sa'
+ def __init__(self, config, verbose=False):
+ ''' Constructor for OpenshiftOC '''
+ super(OCServiceAccountSecret, self).__init__(config.namespace, config.kubeconfig)
+ self.config = config
+ self.verbose = verbose
+ self._service_account = None
+
+ @property
+ def service_account(self):
+ ''' Property for the service account '''
+ if not self._service_account:
+ self.get()
+ return self._service_account
+
+ @service_account.setter
+ def service_account(self, data):
+ ''' setter for the service account '''
+ self._service_account = data
+
+ def exists(self, in_secret):
+ ''' verifies if secret exists in the service account '''
+ result = self.service_account.find_secret(in_secret)
+ if not result:
+ return False
+ return True
+
+ def get(self):
+ ''' get the service account definition from the master '''
+ sao = self._get(OCServiceAccountSecret.kind, self.config.name)
+ if sao['returncode'] == 0:
+ self.service_account = ServiceAccount(content=sao['results'][0])
+ sao['results'] = self.service_account.get('secrets')
+ return sao
+
+ def delete(self):
+ ''' delete secrets '''
+
+ modified = []
+ for rem_secret in self.config.secrets:
+ modified.append(self.service_account.delete_secret(rem_secret))
+
+ if any(modified):
+ return self._replace_content(OCServiceAccountSecret.kind, self.config.name, self.service_account.yaml_dict)
+
+ return {'returncode': 0, 'changed': False}
+
+ def put(self):
+ ''' place secrets into sa '''
+ modified = False
+ for add_secret in self.config.secrets:
+ if not self.service_account.find_secret(add_secret):
+ self.service_account.add_secret(add_secret)
+ modified = True
+
+ if modified:
+ return self._replace_content(OCServiceAccountSecret.kind, self.config.name, self.service_account.yaml_dict)
+
+ return {'returncode': 0, 'changed': 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 '''
+
+ sconfig = ServiceAccountConfig(params['service_account'],
+ params['namespace'],
+ params['kubeconfig'],
+ [params['secret']],
+ None)
+
+ oc_sa_sec = OCServiceAccountSecret(sconfig, verbose=params['debug'])
+
+ state = params['state']
+
+ api_rval = oc_sa_sec.get()
+
+ #####
+ # Get
+ #####
+ if state == 'list':
+ return {'changed': False, 'results': api_rval['results'], 'state': "list"}
+
+ ########
+ # Delete
+ ########
+ if state == 'absent':
+ if oc_sa_sec.exists(params['secret']):
+
+ if check_mode:
+ return {'changed': True, 'msg': 'Would have removed the " + \
+ "secret from the service account.'}
+
+ api_rval = oc_sa_sec.delete()
+
+ return {'changed': True, 'results': api_rval, 'state': "absent"}
+
+ return {'changed': False, 'state': "absent"}
+
+ if state == 'present':
+ ########
+ # Create
+ ########
+ if not oc_sa_sec.exists(params['secret']):
+
+ if check_mode:
+ return {'changed': True, 'msg': 'Would have added the ' + \
+ 'secret to the service account.'}
+
+ # Create it here
+ api_rval = oc_sa_sec.put()
+ if api_rval['returncode'] != 0:
+ return {'failed': True, 'msg': api_rval}
+
+ # return the created object
+ api_rval = oc_sa_sec.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/serviceaccount_secret b/roles/lib_openshift/src/doc/serviceaccount_secret
new file mode 100644
index 000000000..ab90a3f7c
--- /dev/null
+++ b/roles/lib_openshift/src/doc/serviceaccount_secret
@@ -0,0 +1,68 @@
+# flake8: noqa
+# pylint: skip-file
+
+DOCUMENTATION = '''
+---
+module: oc_serviceaccount_secret
+short_description: Module to manage openshift service account secrets
+description:
+ - Manage openshift service account secrets programmatically.
+options:
+ state:
+ description:
+ - If present, the service account will be linked with the secret if it is not already. If absent, the service account will be unlinked from the secret if it is already linked. If list, information about the service account secrets will be gathered and returned as part of the Ansible call results.
+ 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: []
+ service_account:
+ description:
+ - Name of the service account.
+ required: true
+ default: None
+ aliases: []
+ namespace:
+ description:
+ - Namespace of the service account and secret.
+ required: true
+ default: None
+ aliases: []
+ secret:
+ description:
+ - The secret that should be linked to the service account.
+ required: false
+ default: None
+ aliases: []
+author:
+- "Kenny Woodson <kwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+ - name: get secrets of a service account
+ oc_serviceaccount_secret:
+ state: list
+ service_account: builder
+ namespace: default
+ register: sasecretout
+
+
+ - name: Link a service account to a specific secret
+ oc_serviceaccount_secret:
+ service_account: builder
+ secret: mynewsecret
+ namespace: default
+ register: sasecretout
+'''
diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml
index 6438ff4bb..aa02ce120 100644
--- a/roles/lib_openshift/src/sources.yml
+++ b/roles/lib_openshift/src/sources.yml
@@ -84,6 +84,17 @@ oc_serviceaccount.py:
- class/oc_serviceaccount.py
- ansible/oc_serviceaccount.py
+oc_serviceaccount_secret.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/serviceaccount_secret
+- ../../lib_utils/src/class/yedit.py
+- lib/base.py
+- lib/serviceaccount.py
+- class/oc_serviceaccount_secret.py
+- ansible/oc_serviceaccount_secret.py
+
oc_service.py:
- doc/generated
- doc/license
diff --git a/roles/lib_openshift/src/test/integration/oc_serviceaccount_secret.yml b/roles/lib_openshift/src/test/integration/oc_serviceaccount_secret.yml
new file mode 100755
index 000000000..d3bd9f3aa
--- /dev/null
+++ b/roles/lib_openshift/src/test/integration/oc_serviceaccount_secret.yml
@@ -0,0 +1,79 @@
+#!/usr/bin/ansible-playbook --module-path=../../../library/
+---
+- hosts: "{{ cli_master_test }}"
+ gather_facts: no
+ user: root
+
+ vars:
+ namespace: default
+ service_account_name: someserviceaccountname
+ secret_name: somesecretname
+
+ vars_prompt:
+ - name: cli_master_test
+ prompt: "Master to run against"
+ private: false
+ default: localhost
+
+ post_tasks:
+ - name: create service account to test with - Arrange
+ oc_serviceaccount:
+ namespace: "{{ namespace }}"
+ name: "{{ service_account_name }}"
+
+ - name: create secret to test with - Arrange
+ oc_secret:
+ namespace: "{{ namespace }}"
+ name: "{{ secret_name }}"
+ contents:
+ - path: blah
+ data: blahdeblah
+
+ - name: Ensure the service account and secret are not linked - Arrange
+ oc_serviceaccount_secret:
+ state: absent
+ service_account: "{{ service_account_name }}"
+ secret: "{{ secret_name }}"
+ namespace: "{{ namespace }}"
+
+ - name: get secrets of a service account - Act
+ oc_serviceaccount_secret:
+ state: list
+ service_account: builder
+ namespace: "{{ namespace }}"
+ register: sasecretout
+
+ - name: get secrets of a service account - Assert
+ assert:
+ that:
+ - "sasecretout.changed == False"
+ - "sasecretout.state == 'list'"
+ - "sasecretout.results | length > 0"
+
+ - name: Test linking a service account and secret - Act
+ oc_serviceaccount_secret:
+ service_account: "{{ service_account_name }}"
+ secret: "{{ secret_name }}"
+ namespace: "{{ namespace }}"
+ register: sasecretout
+
+ - name: Test linking a service account and secret - Assert
+ assert:
+ that:
+ - "sasecretout.changed == True"
+ - "sasecretout.state == 'present'"
+ - "sasecretout.results.returncode == 0"
+ - "sasecretout.results.results | length > 0"
+
+ - name: Test linking a service account and secret - idempotency - Act
+ oc_serviceaccount_secret:
+ service_account: "{{ service_account_name }}"
+ secret: "{{ secret_name }}"
+ namespace: "{{ namespace }}"
+ register: sasecretout
+
+ - name: Test linking a service account and secret - idempotency - Assert
+ assert:
+ that:
+ - "sasecretout.changed == False"
+ - "sasecretout.state == 'present'"
diff --git a/roles/lib_openshift/src/test/unit/oc_serviceaccount.py b/roles/lib_openshift/src/test/unit/oc_serviceaccount.py
index faf0bfeb5..dab751bb9 100755
--- a/roles/lib_openshift/src/test/unit/oc_serviceaccount.py
+++ b/roles/lib_openshift/src/test/unit/oc_serviceaccount.py
@@ -100,9 +100,9 @@ class OCServiceAccountTest(unittest.TestCase):
# Making sure our mock was called as we expected
mock_cmd.assert_has_calls([
- mock.call(['/usr/bin/oc', '-n', 'default', 'get', 'sa', 'testserviceaccountname', '-o', 'json'], None),
- mock.call(['/usr/bin/oc', '-n', 'default', 'create', '-f', '/tmp/testserviceaccountname'], None),
- mock.call(['/usr/bin/oc', '-n', 'default', 'get', 'sa', 'testserviceaccountname', '-o', 'json'], None),
+ mock.call(['oc', '-n', 'default', 'get', 'sa', 'testserviceaccountname', '-o', 'json'], None),
+ mock.call(['oc', '-n', 'default', 'create', '-f', mock.ANY], None),
+ mock.call(['oc', '-n', 'default', 'get', 'sa', 'testserviceaccountname', '-o', 'json'], None),
])
def tearDown(self):
diff --git a/roles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py b/roles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py
new file mode 100755
index 000000000..342da961b
--- /dev/null
+++ b/roles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python2
+'''
+ Unit tests for oc secret add
+'''
+# To run:
+# ./oc_serviceaccount_secret.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_serviceaccount_secret import OCServiceAccountSecret # noqa: E402
+
+
+class OCServiceAccountSecretTest(unittest.TestCase):
+ '''
+ Test class for OCServiceAccountSecret
+ '''
+
+ def setUp(self):
+ ''' setup method will create a file and set to known configuration '''
+ pass
+
+ @mock.patch('oc_serviceaccount_secret.Yedit._write')
+ @mock.patch('oc_serviceaccount_secret.OCServiceAccountSecret._run')
+ def test_adding_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': 'present',
+ '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"
+ }
+
+ ],
+ "imagePullSecrets": [
+ {
+ "name": "builder-dockercfg-rsrua"
+ }
+ ]
+ }
+ '''
+
+ oc_get_sa_after = '''{
+ "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
+- name: newsecret
+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
+ (0, oc_get_sa_after, ''), # Fourth 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'], 'present')
+
+ # 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.call(['oc', '-n', 'default', 'get', 'sa', 'builder', '-o', 'json'], None)
+ ])
+
+ mock_write.assert_has_calls([
+ mock.call('/tmp/builder', builder_yaml_file)
+ ])
+
+ @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()