mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
[PR #3935/f3be0076 backport][stable-6] Add Gitlab group runners support (#6234)
Add Gitlab group runners support (#3935)
(cherry picked from commit f3be0076af
)
Co-authored-by: Léo GATELLIER <26511053+lgatellier@users.noreply.github.com>
This commit is contained in:
parent
493fa405e2
commit
16a18d1456
4 changed files with 124 additions and 7 deletions
2
changelogs/fragments/3935-add-gitlab-group-runner.yml
Normal file
2
changelogs/fragments/3935-add-gitlab-group-runner.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- 'gitlab_runner - allow to register group runner (https://github.com/ansible-collections/community.general/pull/3935).'
|
|
@ -44,10 +44,17 @@ attributes:
|
||||||
support: none
|
support: none
|
||||||
|
|
||||||
options:
|
options:
|
||||||
|
group:
|
||||||
|
description:
|
||||||
|
- ID or full path of the group in the form group/subgroup.
|
||||||
|
- Mutually exclusive with I(owned) and I(project).
|
||||||
|
type: str
|
||||||
|
version_added: '6.5.0'
|
||||||
project:
|
project:
|
||||||
description:
|
description:
|
||||||
- ID or full path of the project in the form of group/name.
|
- ID or full path of the project in the form of group/name.
|
||||||
- Mutually exclusive with I(owned) since community.general 4.5.0.
|
- Mutually exclusive with I(owned) since community.general 4.5.0.
|
||||||
|
- Mutually exclusive with I(group).
|
||||||
type: str
|
type: str
|
||||||
version_added: '3.7.0'
|
version_added: '3.7.0'
|
||||||
description:
|
description:
|
||||||
|
@ -73,6 +80,7 @@ options:
|
||||||
description:
|
description:
|
||||||
- Searches only runners available to the user when searching for existing, when false admin token required.
|
- Searches only runners available to the user when searching for existing, when false admin token required.
|
||||||
- Mutually exclusive with I(project) since community.general 4.5.0.
|
- Mutually exclusive with I(project) since community.general 4.5.0.
|
||||||
|
- Mutually exclusive with I(group).
|
||||||
default: false
|
default: false
|
||||||
type: bool
|
type: bool
|
||||||
version_added: 2.0.0
|
version_added: 2.0.0
|
||||||
|
@ -209,21 +217,23 @@ except NameError:
|
||||||
|
|
||||||
|
|
||||||
class GitLabRunner(object):
|
class GitLabRunner(object):
|
||||||
def __init__(self, module, gitlab_instance, project=None):
|
def __init__(self, module, gitlab_instance, group=None, project=None):
|
||||||
self._module = module
|
self._module = module
|
||||||
self._gitlab = gitlab_instance
|
self._gitlab = gitlab_instance
|
||||||
|
self.runner_object = None
|
||||||
|
|
||||||
# Whether to operate on GitLab-instance-wide or project-wide runners
|
# Whether to operate on GitLab-instance-wide or project-wide runners
|
||||||
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/60774
|
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/60774
|
||||||
# for group runner token access
|
# for group runner token access
|
||||||
if project:
|
if project:
|
||||||
self._runners_endpoint = project.runners.list
|
self._runners_endpoint = project.runners.list
|
||||||
|
elif group:
|
||||||
|
self._runners_endpoint = group.runners.list
|
||||||
elif module.params['owned']:
|
elif module.params['owned']:
|
||||||
self._runners_endpoint = gitlab_instance.runners.list
|
self._runners_endpoint = gitlab_instance.runners.list
|
||||||
else:
|
else:
|
||||||
self._runners_endpoint = gitlab_instance.runners.all
|
self._runners_endpoint = gitlab_instance.runners.all
|
||||||
|
|
||||||
self.runner_object = None
|
|
||||||
|
|
||||||
def create_or_update_runner(self, description, options):
|
def create_or_update_runner(self, description, options):
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
|
@ -360,6 +370,7 @@ def main():
|
||||||
maximum_timeout=dict(type='int', default=3600),
|
maximum_timeout=dict(type='int', default=3600),
|
||||||
registration_token=dict(type='str', no_log=True),
|
registration_token=dict(type='str', no_log=True),
|
||||||
project=dict(type='str'),
|
project=dict(type='str'),
|
||||||
|
group=dict(type='str'),
|
||||||
state=dict(type='str', default="present", choices=["absent", "present"]),
|
state=dict(type='str', default="present", choices=["absent", "present"]),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -372,6 +383,8 @@ def main():
|
||||||
['api_token', 'api_oauth_token'],
|
['api_token', 'api_oauth_token'],
|
||||||
['api_token', 'api_job_token'],
|
['api_token', 'api_job_token'],
|
||||||
['project', 'owned'],
|
['project', 'owned'],
|
||||||
|
['group', 'owned'],
|
||||||
|
['project', 'group'],
|
||||||
],
|
],
|
||||||
required_together=[
|
required_together=[
|
||||||
['api_username', 'api_password'],
|
['api_username', 'api_password'],
|
||||||
|
@ -396,6 +409,7 @@ def main():
|
||||||
maximum_timeout = module.params['maximum_timeout']
|
maximum_timeout = module.params['maximum_timeout']
|
||||||
registration_token = module.params['registration_token']
|
registration_token = module.params['registration_token']
|
||||||
project = module.params['project']
|
project = module.params['project']
|
||||||
|
group = module.params['group']
|
||||||
|
|
||||||
if access_level is None:
|
if access_level is None:
|
||||||
message = "The option 'access_level' is unspecified, so 'ref_protected' is assumed. "\
|
message = "The option 'access_level' is unspecified, so 'ref_protected' is assumed. "\
|
||||||
|
@ -408,13 +422,20 @@ def main():
|
||||||
|
|
||||||
gitlab_instance = gitlab_authentication(module)
|
gitlab_instance = gitlab_authentication(module)
|
||||||
gitlab_project = None
|
gitlab_project = None
|
||||||
|
gitlab_group = None
|
||||||
|
|
||||||
if project:
|
if project:
|
||||||
try:
|
try:
|
||||||
gitlab_project = gitlab_instance.projects.get(project)
|
gitlab_project = gitlab_instance.projects.get(project)
|
||||||
except gitlab.exceptions.GitlabGetError as e:
|
except gitlab.exceptions.GitlabGetError as e:
|
||||||
module.fail_json(msg='No such a project %s' % project, exception=to_native(e))
|
module.fail_json(msg='No such a project %s' % project, exception=to_native(e))
|
||||||
|
elif group:
|
||||||
|
try:
|
||||||
|
gitlab_group = gitlab_instance.groups.get(group)
|
||||||
|
except gitlab.exceptions.GitlabGetError as e:
|
||||||
|
module.fail_json(msg='No such a group %s' % group, exception=to_native(e))
|
||||||
|
|
||||||
gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_project)
|
gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_group, gitlab_project)
|
||||||
runner_exists = gitlab_runner.exists_runner(runner_description)
|
runner_exists = gitlab_runner.exists_runner(runner_description)
|
||||||
|
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
|
|
|
@ -213,6 +213,31 @@ def resp_get_group(url, request):
|
||||||
return response(200, content, headers, None, 5, request)
|
return response(200, content, headers, None, 5, request)
|
||||||
|
|
||||||
|
|
||||||
|
@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/foo-bar", method="get")
|
||||||
|
def resp_get_group_by_name(url, request):
|
||||||
|
headers = {'content-type': 'application/json'}
|
||||||
|
content = ('{"id": 1, "name": "Foobar Group", "path": "foo-bar",'
|
||||||
|
'"description": "An interesting group", "visibility": "public",'
|
||||||
|
'"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",'
|
||||||
|
'"web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false,'
|
||||||
|
'"full_name": "Foobar Group", "full_path": "foo-bar",'
|
||||||
|
'"project_creation_level": "maintainer", "subgroup_creation_level": "maintainer",'
|
||||||
|
'"require_two_factor_authentication": true,'
|
||||||
|
'"file_template_project_id": 1, "parent_id": null, "projects": [{"id": 1,"description": null, "default_branch": "master",'
|
||||||
|
'"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",'
|
||||||
|
'"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",'
|
||||||
|
'"web_url": "http://example.com/diaspora/diaspora-client",'
|
||||||
|
'"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",'
|
||||||
|
'"tag_list": ["example","disapora client"],"name": "Diaspora Client",'
|
||||||
|
'"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",'
|
||||||
|
'"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",'
|
||||||
|
'"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,'
|
||||||
|
'"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",'
|
||||||
|
'"star_count": 0}]}')
|
||||||
|
content = content.encode("utf-8")
|
||||||
|
return response(200, content, headers, None, 5, request)
|
||||||
|
|
||||||
|
|
||||||
@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get")
|
@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get")
|
||||||
def resp_get_missing_group(url, request):
|
def resp_get_missing_group(url, request):
|
||||||
headers = {'content-type': 'application/json'}
|
headers = {'content-type': 'application/json'}
|
||||||
|
@ -600,6 +625,40 @@ def resp_find_runners_list(url, request):
|
||||||
return response(200, content, headers, None, 5, request)
|
return response(200, content, headers, None, 5, request)
|
||||||
|
|
||||||
|
|
||||||
|
@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/projects/1/runners$', method="get")
|
||||||
|
def resp_find_project_runners(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-20220210","id": 1,'
|
||||||
|
'"is_shared": false,"ip_address": "127.0.0.1","name": null,'
|
||||||
|
'"online": true,"status": "online"},{"active": true,'
|
||||||
|
'"description": "test-2-20220210","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/groups/1/runners$', method="get")
|
||||||
|
def resp_find_group_runners(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-3-20220210","id": 1,'
|
||||||
|
'"is_shared": false,"ip_address": "127.0.0.1","name": null,'
|
||||||
|
'"online": true,"status": "online"},{"active": true,'
|
||||||
|
'"description": "test-4-20220210","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")
|
@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="put")
|
||||||
def resp_update_runner(url, request):
|
def resp_update_runner(url, request):
|
||||||
headers = {'content-type': 'application/json',
|
headers = {'content-type': 'application/json',
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
|
||||||
|
import gitlab
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -23,9 +25,11 @@ try:
|
||||||
from .gitlab import (FakeAnsibleModule,
|
from .gitlab import (FakeAnsibleModule,
|
||||||
GitlabModuleTestCase,
|
GitlabModuleTestCase,
|
||||||
python_version_match_requirement,
|
python_version_match_requirement,
|
||||||
resp_find_runners_all,
|
resp_find_runners_all, resp_find_runners_list,
|
||||||
resp_find_runners_list, resp_get_runner,
|
resp_find_project_runners, resp_find_group_runners,
|
||||||
resp_create_runner, resp_delete_runner)
|
resp_get_runner,
|
||||||
|
resp_create_runner, resp_delete_runner,
|
||||||
|
resp_get_project_by_name, resp_get_group_by_name)
|
||||||
|
|
||||||
# GitLab module requirements
|
# GitLab module requirements
|
||||||
if python_version_match_requirement():
|
if python_version_match_requirement():
|
||||||
|
@ -76,6 +80,37 @@ class TestGitlabRunner(GitlabModuleTestCase):
|
||||||
|
|
||||||
self.assertEqual(rvalue, False)
|
self.assertEqual(rvalue, False)
|
||||||
|
|
||||||
|
@with_httmock(resp_find_project_runners)
|
||||||
|
@with_httmock(resp_get_runner)
|
||||||
|
@with_httmock(resp_get_project_by_name)
|
||||||
|
def test_project_runner_exist(self):
|
||||||
|
gitlab_project = self.gitlab_instance.projects.get('foo-bar/diaspora-client')
|
||||||
|
module_util = GitLabRunner(module=FakeAnsibleModule(), gitlab_instance=self.gitlab_instance, project=gitlab_project)
|
||||||
|
|
||||||
|
rvalue = module_util.exists_runner("test-1-20220210")
|
||||||
|
|
||||||
|
self.assertEqual(rvalue, True)
|
||||||
|
|
||||||
|
rvalue = module_util.exists_runner("test-3-00000000")
|
||||||
|
|
||||||
|
self.assertEqual(rvalue, False)
|
||||||
|
|
||||||
|
@with_httmock(resp_find_group_runners)
|
||||||
|
@with_httmock(resp_get_group_by_name)
|
||||||
|
@with_httmock(resp_get_runner)
|
||||||
|
@pytest.mark.skipif(gitlab.__version__ < "2.3.0", reason="require python-gitlab >= 2.3.0")
|
||||||
|
def test_group_runner_exist(self):
|
||||||
|
gitlab_group = self.gitlab_instance.groups.get('foo-bar')
|
||||||
|
module_util = GitLabRunner(module=FakeAnsibleModule(), gitlab_instance=self.gitlab_instance, group=gitlab_group)
|
||||||
|
|
||||||
|
rvalue = module_util.exists_runner("test-3-20220210")
|
||||||
|
|
||||||
|
self.assertEqual(rvalue, True)
|
||||||
|
|
||||||
|
rvalue = module_util.exists_runner("test-3-00000000")
|
||||||
|
|
||||||
|
self.assertEqual(rvalue, False)
|
||||||
|
|
||||||
@with_httmock(resp_create_runner)
|
@with_httmock(resp_create_runner)
|
||||||
def test_create_runner(self):
|
def test_create_runner(self):
|
||||||
runner = self.module_util_all.create_runner({"token": "token", "description": "test-1-20150125"})
|
runner = self.module_util_all.create_runner({"token": "token", "description": "test-1-20150125"})
|
||||||
|
|
Loading…
Reference in a new issue