summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Meier <r.meier@siemens.com>2020-05-20 15:42:39 +0200
committerGitHub <noreply@github.com>2020-05-20 15:42:39 +0200
commit38e9fde46a2e9e630154feb1cc533a75a55e4a2a (patch)
tree1b2a86206af2a2d05e7a121eb4df5023d06a028d
parent89007c9d1a642bda87ca086f00acf0f47d663611 (diff)
parent127fa5a2134aee82958ce05357d60513569c3659 (diff)
downloadgitlab-38e9fde46a2e9e630154feb1cc533a75a55e4a2a.tar.gz
Merge pull request #1089 from python-gitlab/feat/group-runners
feat: add group runners api
-rw-r--r--README.rst5
-rw-r--r--docs/gl_objects/runners.rst5
-rw-r--r--gitlab/tests/conftest.py12
-rw-r--r--gitlab/tests/objects/test_runners.py277
-rw-r--r--gitlab/v4/objects.py15
-rw-r--r--test-requirements.txt1
6 files changed, 313 insertions, 2 deletions
diff --git a/README.rst b/README.rst
index 7aa0904..d8a0358 100644
--- a/README.rst
+++ b/README.rst
@@ -128,6 +128,11 @@ Before submitting a pull request make sure that the tests still succeed with
your change. Unit tests and functional tests run using the travis service and
passing tests are mandatory to get merge requests accepted.
+We're currently in a restructing phase for the unit tests. If you're changing existing
+tests, feel free to keep the current format. Otherwise please write new tests with pytest and
+using `responses<https://github.com/getsentry/responses>`_. An example for new tests can be found in
+tests/objects/test_runner.py
+
You need to install ``tox`` to run unit tests and documentation builds locally:
.. code-block:: bash
diff --git a/docs/gl_objects/runners.rst b/docs/gl_objects/runners.rst
index ceda32a..b369bed 100644
--- a/docs/gl_objects/runners.rst
+++ b/docs/gl_objects/runners.rst
@@ -78,7 +78,7 @@ Verify a registered runner token::
except GitlabVerifyError:
print("Invalid token")
-Project runners
+Project/Group runners
===============
Reference
@@ -89,6 +89,9 @@ Reference
+ :class:`gitlab.v4.objects.ProjectRunner`
+ :class:`gitlab.v4.objects.ProjectRunnerManager`
+ :attr:`gitlab.v4.objects.Project.runners`
+ + :class:`gitlab.v4.objects.GroupRunner`
+ + :class:`gitlab.v4.objects.GroupRunnerManager`
+ + :attr:`gitlab.v4.objects.Group.runners`
* GitLab API: https://docs.gitlab.com/ce/api/runners.html
diff --git a/gitlab/tests/conftest.py b/gitlab/tests/conftest.py
new file mode 100644
index 0000000..91752c6
--- /dev/null
+++ b/gitlab/tests/conftest.py
@@ -0,0 +1,12 @@
+import pytest
+import gitlab
+
+
+@pytest.fixture
+def gl():
+ return gitlab.Gitlab(
+ "http://localhost",
+ private_token="private_token",
+ ssl_verify=True,
+ api_version=4,
+ )
diff --git a/gitlab/tests/objects/test_runners.py b/gitlab/tests/objects/test_runners.py
new file mode 100644
index 0000000..2f86bef
--- /dev/null
+++ b/gitlab/tests/objects/test_runners.py
@@ -0,0 +1,277 @@
+import unittest
+import responses
+import gitlab
+import pytest
+import re
+from .mocks import * # noqa
+
+
+runner_detail = {
+ "active": True,
+ "architecture": "amd64",
+ "description": "test-1-20150125",
+ "id": 6,
+ "ip_address": "127.0.0.1",
+ "is_shared": False,
+ "contacted_at": "2016-01-25T16:39:48.066Z",
+ "name": "test-runner",
+ "online": True,
+ "status": "online",
+ "platform": "linux",
+ "projects": [
+ {
+ "id": 1,
+ "name": "GitLab Community Edition",
+ "name_with_namespace": "GitLab.org / GitLab Community Edition",
+ "path": "gitlab-foss",
+ "path_with_namespace": "gitlab-org/gitlab-foss",
+ }
+ ],
+ "revision": "5nj35",
+ "tag_list": ["ruby", "mysql"],
+ "version": "v13.0.0",
+ "access_level": "ref_protected",
+ "maximum_timeout": 3600,
+}
+
+runner_shortinfo = {
+ "active": True,
+ "description": "test-1-20150125",
+ "id": 6,
+ "is_shared": False,
+ "ip_address": "127.0.0.1",
+ "name": "test-name",
+ "online": True,
+ "status": "online",
+}
+
+runner_jobs = [
+ {
+ "id": 6,
+ "ip_address": "127.0.0.1",
+ "status": "running",
+ "stage": "test",
+ "name": "test",
+ "ref": "master",
+ "tag": False,
+ "coverage": "99%",
+ "created_at": "2017-11-16T08:50:29.000Z",
+ "started_at": "2017-11-16T08:51:29.000Z",
+ "finished_at": "2017-11-16T08:53:29.000Z",
+ "duration": 120,
+ "user": {
+ "id": 1,
+ "name": "John Doe2",
+ "username": "user2",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
+ "web_url": "http://localhost/user2",
+ "created_at": "2017-11-16T18:38:46.000Z",
+ "bio": None,
+ "location": None,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": None,
+ },
+ }
+]
+
+
+@pytest.fixture
+def resp_get_runners_jobs():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/runners/6/jobs",
+ json=runner_jobs,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_get_runners_list():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url=re.compile(r".*?(/runners(/all)?|/(groups|projects)/1/runners)"),
+ json=[runner_shortinfo],
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_detail():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?/runners/6")
+ rsps.add(
+ method=responses.GET,
+ url=pattern,
+ json=runner_detail,
+ content_type="application/json",
+ status=200,
+ )
+ rsps.add(
+ method=responses.PUT,
+ url=pattern,
+ json=runner_detail,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_register():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?/runners")
+ rsps.add(
+ method=responses.POST,
+ url=pattern,
+ json={"id": "6", "token": "6337ff461c94fd3fa32ba3b1ff4125"},
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_enable():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?(projects|groups)/1/runners")
+ rsps.add(
+ method=responses.POST,
+ url=pattern,
+ json=runner_shortinfo,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_delete():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?/runners/6")
+ rsps.add(
+ method=responses.GET,
+ url=pattern,
+ json=runner_detail,
+ content_type="application/json",
+ status=200,
+ )
+ rsps.add(
+ method=responses.DELETE, url=pattern, status=204,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_disable():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?/(groups|projects)/1/runners/6")
+ rsps.add(
+ method=responses.DELETE, url=pattern, status=204,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_runner_verify():
+ with responses.RequestsMock() as rsps:
+ pattern = re.compile(r".*?/runners/verify")
+ rsps.add(
+ method=responses.POST, url=pattern, status=200,
+ )
+ yield rsps
+
+
+def test_owned_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
+ runners = gl.runners.list()
+ assert runners[0].active == True
+ assert runners[0].id == 6
+ assert runners[0].name == "test-name"
+ assert len(runners) == 1
+
+
+def test_project_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
+ runners = gl.projects.get(1, lazy=True).runners.list()
+ assert runners[0].active == True
+ assert runners[0].id == 6
+ assert runners[0].name == "test-name"
+ assert len(runners) == 1
+
+
+def test_group_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
+ runners = gl.groups.get(1, lazy=True).runners.list()
+ assert runners[0].active == True
+ assert runners[0].id == 6
+ assert runners[0].name == "test-name"
+ assert len(runners) == 1
+
+
+def test_all_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
+ runners = gl.runners.all()
+ assert runners[0].active == True
+ assert runners[0].id == 6
+ assert runners[0].name == "test-name"
+ assert len(runners) == 1
+
+
+def test_create_runner(gl: gitlab.Gitlab, resp_runner_register):
+ runner = gl.runners.create({"token": "token"})
+ assert runner.id == "6"
+ assert runner.token == "6337ff461c94fd3fa32ba3b1ff4125"
+
+
+def test_get_update_runner(gl: gitlab.Gitlab, resp_runner_detail):
+ runner = gl.runners.get(6)
+ assert runner.active == True
+ runner.tag_list.append("new")
+ runner.save()
+
+
+def test_remove_runner(gl: gitlab.Gitlab, resp_runner_delete):
+ runner = gl.runners.get(6)
+ runner.delete()
+ gl.runners.delete(6)
+
+
+def test_disable_project_runner(gl: gitlab.Gitlab, resp_runner_disable):
+ gl.projects.get(1, lazy=True).runners.delete(6)
+
+
+def test_disable_group_runner(gl: gitlab.Gitlab, resp_runner_disable):
+ gl.groups.get(1, lazy=True).runners.delete(6)
+
+
+def test_enable_project_runner(gl: gitlab.Gitlab, resp_runner_enable):
+ runner = gl.projects.get(1, lazy=True).runners.create({"runner_id": 6})
+ assert runner.active == True
+ assert runner.id == 6
+ assert runner.name == "test-name"
+
+
+def test_enable_group_runner(gl: gitlab.Gitlab, resp_runner_enable):
+ runner = gl.groups.get(1, lazy=True).runners.create({"runner_id": 6})
+ assert runner.active == True
+ assert runner.id == 6
+ assert runner.name == "test-name"
+
+
+def test_verify_runner(gl: gitlab.Gitlab, resp_runner_verify):
+ gl.runners.verify("token")
+
+
+def test_runner_jobs(gl: gitlab.Gitlab, resp_get_runners_jobs):
+ jobs = gl.runners.get(6, lazy=True).jobs.list()
+ assert jobs[0].duration == 120
+ assert jobs[0].name == "test"
+ assert jobs[0].user.get("name") == "John Doe2"
+ assert len(jobs) == 1
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index 42b2bf4..bc45bf1 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -1308,6 +1308,17 @@ class GroupProjectManager(ListMixin, RESTManager):
)
+class GroupRunner(ObjectDeleteMixin, RESTObject):
+ pass
+
+
+class GroupRunnerManager(NoUpdateMixin, RESTManager):
+ _path = "/groups/%(group_id)s/runners"
+ _obj_cls = GroupRunner
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("runner_id",), tuple())
+
+
class GroupSubgroup(RESTObject):
pass
@@ -1357,6 +1368,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
("milestones", "GroupMilestoneManager"),
("notificationsettings", "GroupNotificationSettingsManager"),
("projects", "GroupProjectManager"),
+ ("runners", "GroupRunnerManager"),
("subgroups", "GroupSubgroupManager"),
("variables", "GroupVariableManager"),
("clusters", "GroupClusterManager"),
@@ -5382,7 +5394,8 @@ class RunnerManager(CRUDMixin, RESTManager):
query_data = {}
if scope is not None:
query_data["scope"] = scope
- return self.gitlab.http_list(path, query_data, **kwargs)
+ obj = self.gitlab.http_list(path, query_data, **kwargs)
+ return [self._obj_cls(self, item) for item in obj]
@cli.register_custom_action("RunnerManager", ("token",))
@exc.on_http_error(exc.GitlabVerifyError)
diff --git a/test-requirements.txt b/test-requirements.txt
index c788436..ed5d639 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,3 +7,4 @@ pytest
pytest-cov
sphinx>=1.3
sphinx_rtd_theme
+responses