Skip to content

Commit 6d31649

Browse files
nejchJohnVillalovos
authored andcommitted
feat(api): add support for container registry protection rules
1 parent d509da6 commit 6d31649

File tree

7 files changed

+197
-0
lines changed

7 files changed

+197
-0
lines changed

docs/api-objects.rst

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ API examples
4848
gl_objects/projects
4949
gl_objects/project_access_tokens
5050
gl_objects/protected_branches
51+
gl_objects/protected_container_repositories
5152
gl_objects/protected_environments
5253
gl_objects/protected_packages
5354
gl_objects/releases
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
################################
2+
Protected container repositories
3+
################################
4+
5+
You can list and manage container registry protection rules in a project.
6+
7+
References
8+
----------
9+
10+
* v4 API:
11+
12+
+ :class:`gitlab.v4.objects.ProjectRegistryProtectionRuleRule`
13+
+ :class:`gitlab.v4.objects.ProjectRegistryProtectionRuleRuleManager`
14+
+ :attr:`gitlab.v4.objects.Project.registry_protection_rules`
15+
16+
* GitLab API: https://docs.gitlab.com/ee/api/project_container_registry_protection_rules.html
17+
18+
Examples
19+
--------
20+
21+
List the container registry protection rules for a project::
22+
23+
registry_rules = project.registry_protection_rules.list()
24+
25+
Create a container registry protection rule::
26+
27+
registry_rule = project.registry_protection_rules.create(
28+
{
29+
'repository_path_pattern': 'test/image',
30+
'minimum_access_level_for_push': 'maintainer',
31+
'minimum_access_level_for_delete': 'maintainer',
32+
}
33+
)
34+
35+
Update a container registry protection rule::
36+
37+
registry_rule.minimum_access_level_for_push = 'owner'
38+
registry_rule.save()
39+
40+
Delete a container registry protection rule::
41+
42+
registry_rule = project.registry_protection_rules.delete(registry_rule.id)
43+
# or
44+
registry_rule.delete()

gitlab/v4/objects/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
from .project_access_tokens import *
5555
from .projects import *
5656
from .push_rules import *
57+
from .registry_protection_rules import *
5758
from .releases import *
5859
from .repositories import *
5960
from .resource_groups import *

gitlab/v4/objects/projects.py

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@
8585
)
8686
from .project_access_tokens import ProjectAccessTokenManager # noqa: F401
8787
from .push_rules import ProjectPushRulesManager # noqa: F401
88+
from .registry_protection_rules import ( # noqa: F401
89+
ProjectRegistryProtectionRuleManager,
90+
)
8891
from .releases import ProjectReleaseManager # noqa: F401
8992
from .repositories import RepositoryMixin
9093
from .resource_groups import ProjectResourceGroupManager
@@ -218,6 +221,7 @@ class Project(
218221
protectedbranches: ProjectProtectedBranchManager
219222
protectedtags: ProjectProtectedTagManager
220223
pushrules: ProjectPushRulesManager
224+
registry_protection_rules: ProjectRegistryProtectionRuleManager
221225
releases: ProjectReleaseManager
222226
resource_groups: ProjectResourceGroupManager
223227
remote_mirrors: "ProjectRemoteMirrorManager"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from gitlab.base import RESTManager, RESTObject
2+
from gitlab.mixins import CreateMixin, ListMixin, SaveMixin, UpdateMethod, UpdateMixin
3+
from gitlab.types import RequiredOptional
4+
5+
__all__ = [
6+
"ProjectRegistryProtectionRule",
7+
"ProjectRegistryProtectionRuleManager",
8+
]
9+
10+
11+
class ProjectRegistryProtectionRule(SaveMixin, RESTObject):
12+
_repr_attr = "repository_path_pattern"
13+
14+
15+
class ProjectRegistryProtectionRuleManager(
16+
ListMixin, CreateMixin, UpdateMixin, RESTManager
17+
):
18+
_path = "/projects/{project_id}/registry/protection/rules"
19+
_obj_cls = ProjectRegistryProtectionRule
20+
_from_parent_attrs = {"project_id": "id"}
21+
_create_attrs = RequiredOptional(
22+
required=("repository_path_pattern",),
23+
optional=(
24+
"minimum_access_level_for_push",
25+
"minimum_access_level_for_delete",
26+
),
27+
)
28+
_update_attrs = RequiredOptional(
29+
optional=(
30+
"repository_path_pattern",
31+
"minimum_access_level_for_push",
32+
"minimum_access_level_for_delete",
33+
),
34+
)
35+
_update_method = UpdateMethod.PATCH

tests/functional/api/test_registry.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
3+
from gitlab import Gitlab
4+
from gitlab.v4.objects import Project, ProjectRegistryProtectionRule
5+
6+
7+
@pytest.fixture(scope="module", autouse=True)
8+
def protected_registry_feature(gl: Gitlab):
9+
gl.features.set(name="container_registry_protected_containers", value=True)
10+
11+
12+
def test_list_project_protected_registries(project: Project):
13+
rules = project.registry_protection_rules.list()
14+
assert isinstance(rules, list)
15+
16+
17+
@pytest.mark.skip(reason="Not released yet")
18+
def test_create_project_protected_registry(project: Project):
19+
protected_registry = project.registry_protection_rules.create(
20+
{
21+
"repository_path_pattern": "test/image",
22+
"minimum_access_level_for_push": "maintainer",
23+
}
24+
)
25+
assert isinstance(protected_registry, ProjectRegistryProtectionRule)
26+
assert protected_registry.repository_path_pattern == "test/image"
27+
28+
protected_registry.minimum_access_level_for_push = "owner"
29+
protected_registry.save()
30+
assert protected_registry.minimum_access_level_for_push == "owner"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
GitLab API: https://docs.gitlab.com/ee/api/project_container_registry_protection_rules.html
3+
"""
4+
5+
import pytest
6+
import responses
7+
8+
from gitlab.v4.objects import ProjectRegistryProtectionRule
9+
10+
protected_registry_content = {
11+
"id": 1,
12+
"project_id": 7,
13+
"repository_path_pattern": "test/image",
14+
"minimum_access_level_for_push": "maintainer",
15+
"minimum_access_level_for_delete": "maintainer",
16+
}
17+
18+
19+
@pytest.fixture
20+
def resp_list_protected_registries():
21+
with responses.RequestsMock() as rsps:
22+
rsps.add(
23+
method=responses.GET,
24+
url="http://localhost/api/v4/projects/1/registry/protection/rules",
25+
json=[protected_registry_content],
26+
content_type="application/json",
27+
status=200,
28+
)
29+
yield rsps
30+
31+
32+
@pytest.fixture
33+
def resp_create_protected_registry():
34+
with responses.RequestsMock() as rsps:
35+
rsps.add(
36+
method=responses.POST,
37+
url="http://localhost/api/v4/projects/1/registry/protection/rules",
38+
json=protected_registry_content,
39+
content_type="application/json",
40+
status=201,
41+
)
42+
yield rsps
43+
44+
45+
@pytest.fixture
46+
def resp_update_protected_registry():
47+
updated_content = protected_registry_content.copy()
48+
updated_content["repository_path_pattern"] = "abc*"
49+
50+
with responses.RequestsMock() as rsps:
51+
rsps.add(
52+
method=responses.PATCH,
53+
url="http://localhost/api/v4/projects/1/registry/protection/rules/1",
54+
json=updated_content,
55+
content_type="application/json",
56+
status=200,
57+
)
58+
yield rsps
59+
60+
61+
def test_list_project_protected_registries(project, resp_list_protected_registries):
62+
protected_registry = project.registry_protection_rules.list()[0]
63+
assert isinstance(protected_registry, ProjectRegistryProtectionRule)
64+
assert protected_registry.repository_path_pattern == "test/image"
65+
66+
67+
def test_create_project_protected_registry(project, resp_create_protected_registry):
68+
protected_registry = project.registry_protection_rules.create(
69+
{
70+
"repository_path_pattern": "test/image",
71+
"minimum_access_level_for_push": "maintainer",
72+
}
73+
)
74+
assert isinstance(protected_registry, ProjectRegistryProtectionRule)
75+
assert protected_registry.repository_path_pattern == "test/image"
76+
77+
78+
def test_update_project_protected_registry(project, resp_update_protected_registry):
79+
updated = project.registry_protection_rules.update(
80+
1, {"repository_path_pattern": "abc*"}
81+
)
82+
assert updated["repository_path_pattern"] == "abc*"

0 commit comments

Comments
 (0)