Skip to content

Commit

Permalink
Add Gitlab group runners support
Browse files Browse the repository at this point in the history
  • Loading branch information
lgatellier committed Dec 6, 2022
1 parent 3ebc281 commit 31194d1
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 14 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/3935-add-gitlab-group-runner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- 'gitlab_runner - allow to register group runner (https://github.com/ansible-collections/community.general/pull/3935).'
29 changes: 18 additions & 11 deletions plugins/modules/gitlab_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,10 @@
- community.general.gitlab
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.2.0'
project:
description:
- 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(group) since community.general 6.2.0.
type: str
version_added: '3.7.0'
description:
Expand All @@ -73,6 +66,7 @@
description:
- 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(group) since community.general 6.2.0.
default: false
type: bool
version_added: 2.0.0
Expand Down Expand Up @@ -196,21 +190,23 @@ def cmp(a, b):


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._gitlab = gitlab_instance
self.runner_object = None

# Whether to operate on GitLab-instance-wide or project-wide runners
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/60774
# for group runner token access
if project:
self._runners_endpoint = project.runners.list
elif group:
self._runners_endpoint = group.runners.list
elif module.params['owned']:
self._runners_endpoint = gitlab_instance.runners.list
else:
self._runners_endpoint = gitlab_instance.runners.all

self.runner_object = None

def create_or_update_runner(self, description, options):
changed = False

Expand Down Expand Up @@ -338,6 +334,7 @@ def main():
maximum_timeout=dict(type='int', default=3600),
registration_token=dict(type='str', no_log=True),
project=dict(type='str'),
group=dict(type='str'),
state=dict(type='str', default="present", choices=["absent", "present"]),
))

Expand All @@ -350,6 +347,8 @@ def main():
['api_token', 'api_oauth_token'],
['api_token', 'api_job_token'],
['project', 'owned'],
['group', 'owned'],
['project', 'group'],
],
required_together=[
['api_username', 'api_password'],
Expand All @@ -374,16 +373,24 @@ def main():
maximum_timeout = module.params['maximum_timeout']
registration_token = module.params['registration_token']
project = module.params['project']
group = module.params['group']

gitlab_instance = gitlab_authentication(module)
gitlab_project = None
gitlab_group = None

if project:
try:
gitlab_project = gitlab_instance.projects.get(project)
except gitlab.exceptions.GitlabGetError as 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)

if state == 'absent':
Expand Down
59 changes: 59 additions & 0 deletions tests/unit/plugins/modules/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@ def resp_get_group(url, 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")
def resp_get_missing_group(url, request):
headers = {'content-type': 'application/json'}
Expand Down Expand Up @@ -600,6 +625,40 @@ def resp_find_runners_list(url, 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")
def resp_update_runner(url, request):
headers = {'content-type': 'application/json',
Expand Down
41 changes: 38 additions & 3 deletions tests/unit/plugins/modules/test_gitlab_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import (absolute_import, division, print_function)

import gitlab
__metaclass__ = type

import pytest
Expand All @@ -23,9 +25,11 @@ def _dummy(x):
from .gitlab import (FakeAnsibleModule,
GitlabModuleTestCase,
python_version_match_requirement,
resp_find_runners_all,
resp_find_runners_list, resp_get_runner,
resp_create_runner, resp_delete_runner)
resp_find_runners_all, resp_find_runners_list,
resp_find_project_runners, resp_find_group_runners,
resp_get_runner,
resp_create_runner, resp_delete_runner,
resp_get_project_by_name, resp_get_group_by_name)

# GitLab module requirements
if python_version_match_requirement():
Expand Down Expand Up @@ -76,6 +80,37 @@ def test_runner_exist_owned(self):

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)
def test_create_runner(self):
runner = self.module_util_all.create_runner({"token": "token", "description": "test-1-20150125"})
Expand Down

0 comments on commit 31194d1

Please sign in to comment.