From 11cb13697138f575020a3dd8c4159d889c041445 Mon Sep 17 00:00:00 2001 From: Marcus Watkins Date: Sun, 24 Jan 2021 13:02:44 -0700 Subject: [PATCH] Allow gitlab_runner to function for non-admin users (#1491) * Allow gitlab_runner to function for non-admin users * Fix errant whitespace * Allow later python-gitlab versions * gitlab_runner: Switch to feature instead of bugfix, add examples * Fix version_added in gitlab_runner * Cleanup documentation for gitlab_runner fix * Update plugins/modules/source_control/gitlab/gitlab_runner.py Co-authored-by: Brendan Batliner * Update plugins/modules/source_control/gitlab/gitlab_runner.py Co-authored-by: Felix Fontein * Revert docs change * Clarify docs * Clarify docs Co-authored-by: Brendan Batliner Co-authored-by: Felix Fontein --- .../1491-gitlab-runner-owned-parameter.yml | 5 ++ .../source_control/gitlab/gitlab_runner.py | 40 +++++++++++--- .../modules/source_control/gitlab/gitlab.py | 54 +++++++++++++------ .../gitlab/test_gitlab_runner.py | 20 +++++-- tests/unit/requirements.txt | 2 +- 5 files changed, 93 insertions(+), 28 deletions(-) create mode 100644 changelogs/fragments/1491-gitlab-runner-owned-parameter.yml diff --git a/changelogs/fragments/1491-gitlab-runner-owned-parameter.yml b/changelogs/fragments/1491-gitlab-runner-owned-parameter.yml new file mode 100644 index 0000000000..2d9eb403f6 --- /dev/null +++ b/changelogs/fragments/1491-gitlab-runner-owned-parameter.yml @@ -0,0 +1,5 @@ +--- +minor_changes: + - gitlab_runner - add ``owned`` option to allow non-admin use (https://github.com/ansible-collections/community.general/pull/1491). +bugfixes: + - gitlab_runner - fix compatiblity with some versions of python-gitlab (https://github.com/ansible-collections/community.general/pull/1491). diff --git a/plugins/modules/source_control/gitlab/gitlab_runner.py b/plugins/modules/source_control/gitlab/gitlab_runner.py index cc2911d05f..8ebd1a3851 100644 --- a/plugins/modules/source_control/gitlab/gitlab_runner.py +++ b/plugins/modules/source_control/gitlab/gitlab_runner.py @@ -57,6 +57,12 @@ options: - The registration token is used to register new runners. required: True type: str + owned: + description: + - Searches only runners available to the user when searching for existing, when false admin token required. + default: no + type: bool + version_added: 2.0.0 active: description: - Define if the runners is immediately active after creation. @@ -114,6 +120,14 @@ EXAMPLES = ''' api_token: "{{ access_token }}" description: Docker Machine t1 state: absent + +- name: Delete an owned runner as a non-admin + community.general.gitlab_runner: + api_url: https://gitlab.example.com/ + api_token: "{{ access_token }}" + description: Docker Machine t1 + owned: yes + state: absent ''' RETURN = ''' @@ -246,18 +260,28 @@ class GitLabRunner(object): ''' @param description Description of the runner ''' - def findRunner(self, description): - runners = self._gitlab.runners.all(as_list=False) + def findRunner(self, description, owned=False): + if owned: + runners = self._gitlab.runners.list(as_list=False) + else: + runners = self._gitlab.runners.all(as_list=False) + for runner in runners: - if (runner['description'] == description): - return self._gitlab.runners.get(runner['id']) + # python-gitlab 2.2 through at least 2.5 returns a list of dicts for list() instead of a Runner + # object, so we need to handle both + if hasattr(runner, "description"): + if (runner.description == description): + return self._gitlab.runners.get(runner.id) + else: + if (runner['description'] == description): + return self._gitlab.runners.get(runner['id']) ''' @param description Description of the runner ''' - def existsRunner(self, description): + def existsRunner(self, description, owned=False): # When runner exists, object will be stored in self.runnerObject. - runner = self.findRunner(description) + runner = self.findRunner(description, owned) if runner: self.runnerObject = runner @@ -279,6 +303,7 @@ def main(): api_token=dict(type='str', no_log=True), description=dict(type='str', required=True, aliases=["name"]), active=dict(type='bool', default=True), + owned=dict(type='bool', default=False), tag_list=dict(type='list', default=[]), run_untagged=dict(type='bool', default=True), locked=dict(type='bool', default=False), @@ -304,6 +329,7 @@ def main(): ) state = module.params['state'] + owned = module.params['owned'] runner_description = module.params['description'] runner_active = module.params['active'] tag_list = module.params['tag_list'] @@ -319,7 +345,7 @@ def main(): gitlab_instance = gitlabAuthentication(module) gitlab_runner = GitLabRunner(module, gitlab_instance) - runner_exists = gitlab_runner.existsRunner(runner_description) + runner_exists = gitlab_runner.existsRunner(runner_description, owned) if state == 'absent': if runner_exists: diff --git a/tests/unit/plugins/modules/source_control/gitlab/gitlab.py b/tests/unit/plugins/modules/source_control/gitlab/gitlab.py index 77b1075cc8..eb8099d37b 100644 --- a/tests/unit/plugins/modules/source_control/gitlab/gitlab.py +++ b/tests/unit/plugins/modules/source_control/gitlab/gitlab.py @@ -524,20 +524,8 @@ RUNNER API ''' -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/runners/all", method="get") +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/all$', method="get") def resp_find_runners_all(url, request): - headers = {'content-type': 'application/json'} - content = ('[{"active": true,"description": "test-1-20150125","id": 1,' - '"is_shared": false,"ip_address": "127.0.0.1","name": null,' - '"online": true,"status": "online"},{"active": true,' - '"description": "test-2-20150125","id": 2,"ip_address": "127.0.0.1",' - '"is_shared": false,"name": null,"online": false,"status": "offline"}]') - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/runners", method="get") -def resp_find_runners_list(url, request): headers = {'content-type': 'application/json', "X-Page": 1, "X-Next-Page": 2, @@ -553,7 +541,41 @@ def resp_find_runners_list(url, request): return response(200, content, headers, None, 5, request) -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/runners/1", method="get") +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners$', method="get") +def resp_find_runners_list(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20201214","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20201214","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="put") +def resp_update_runner(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20201214","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20201214","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="get") def resp_get_runner(url, request): headers = {'content-type': 'application/json'} content = ('{"active": true,"description": "test-1-20150125","id": 1,' @@ -563,7 +585,7 @@ def resp_get_runner(url, request): return response(200, content, headers, None, 5, request) -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/runners", method="post") +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners$', method="post") def resp_create_runner(url, request): headers = {'content-type': 'application/json'} content = ('{"active": true,"description": "test-1-20150125","id": 1,' @@ -573,7 +595,7 @@ def resp_create_runner(url, request): return response(201, content, headers, None, 5, request) -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/runners/1", method="delete") +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="delete") def resp_delete_runner(url, request): headers = {'content-type': 'application/json'} content = ('{}') diff --git a/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_runner.py b/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_runner.py index 6af406f496..fb192981e8 100644 --- a/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_runner.py +++ b/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_runner.py @@ -21,6 +21,7 @@ pytestmark = [] try: from .gitlab import (GitlabModuleTestCase, python_version_match_requirement, + resp_find_runners_all, resp_find_runners_list, resp_get_runner, resp_create_runner, resp_delete_runner) @@ -50,9 +51,9 @@ class TestGitlabRunner(GitlabModuleTestCase): self.moduleUtil = GitLabRunner(module=self.mock_module, gitlab_instance=self.gitlab_instance) - @with_httmock(resp_find_runners_list) + @with_httmock(resp_find_runners_all) @with_httmock(resp_get_runner) - def test_runner_exist(self): + def test_runner_exist_all(self): rvalue = self.moduleUtil.existsRunner("test-1-20150125") self.assertEqual(rvalue, True) @@ -61,6 +62,17 @@ class TestGitlabRunner(GitlabModuleTestCase): self.assertEqual(rvalue, False) + @with_httmock(resp_find_runners_list) + @with_httmock(resp_get_runner) + def test_runner_exist_owned(self): + rvalue = self.moduleUtil.existsRunner("test-1-20201214", True) + + self.assertEqual(rvalue, True) + + rvalue = self.moduleUtil.existsRunner("test-3-00000000", True) + + self.assertEqual(rvalue, False) + @with_httmock(resp_create_runner) def test_create_runner(self): runner = self.moduleUtil.createRunner({"token": "token", "description": "test-1-20150125"}) @@ -68,7 +80,7 @@ class TestGitlabRunner(GitlabModuleTestCase): self.assertEqual(type(runner), Runner) self.assertEqual(runner.description, "test-1-20150125") - @with_httmock(resp_find_runners_list) + @with_httmock(resp_find_runners_all) @with_httmock(resp_get_runner) def test_update_runner(self): runner = self.moduleUtil.findRunner("test-1-20150125") @@ -84,7 +96,7 @@ class TestGitlabRunner(GitlabModuleTestCase): self.assertEqual(changed, False) self.assertEqual(newRunner.description, "Runner description") - @with_httmock(resp_find_runners_list) + @with_httmock(resp_find_runners_all) @with_httmock(resp_get_runner) @with_httmock(resp_delete_runner) def test_delete_runner(self): diff --git a/tests/unit/requirements.txt b/tests/unit/requirements.txt index b87396709e..968820807d 100644 --- a/tests/unit/requirements.txt +++ b/tests/unit/requirements.txt @@ -12,7 +12,7 @@ linode-python # APIv3 linode_api4 ; python_version > '2.6' # APIv4 # requirement for the gitlab module -python-gitlab < 2.3.0 # version 2.3.0 makes gitlab_runner tests fail +python-gitlab httmock # requirement for maven_artifact module