qontract-reconcile 0.10.1rc1030__py3-none-any.whl → 0.10.1rc1031__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/RECORD +8 -8
- reconcile/gitlab_permissions.py +22 -130
- reconcile/test/test_gitlab_permissions.py +9 -97
- reconcile/utils/gitlab_api.py +43 -7
- {qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc1031
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Home-page: https://github.com/app-sre/qontract-reconcile
|
6
6
|
Author: Red Hat App-SRE Team
|
{qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/RECORD
RENAMED
@@ -35,7 +35,7 @@ reconcile/gitlab_labeler.py,sha256=4xJHmVX155fclrHqkR926sL1GH6RTN5XfZ8PnqNXbRA,4
|
|
35
35
|
reconcile/gitlab_members.py,sha256=PrJE9OhDRdGG_gHM_77nQojLb4B18jtUu8DxgLsRS88,8417
|
36
36
|
reconcile/gitlab_mr_sqs_consumer.py,sha256=O46mdziPgGOndbU-0_UJKJVUaiEoVzJPEgKm4_UvYoI,2571
|
37
37
|
reconcile/gitlab_owners.py,sha256=sn9njaKOtqcvnhi2qtm-faAfAR4zNqflbSuusA9RUuI,13456
|
38
|
-
reconcile/gitlab_permissions.py,sha256=
|
38
|
+
reconcile/gitlab_permissions.py,sha256=wq0jbWWPN5iOWnRLuZeemCgKh4a_iGy2d4Iq3WFJykM,3636
|
39
39
|
reconcile/gitlab_projects.py,sha256=K3tFf_aD1W4Ijp5q-9Qek3kwFGEWPcZ1kd7tzFJ4GyQ,1781
|
40
40
|
reconcile/integrations_manager.py,sha256=gvOhVklJDeMPURxLjV30Q4hnLET3BZ-NeEEtQBoo_E0,9500
|
41
41
|
reconcile/jenkins_base.py,sha256=0Gocu3fU2YTltaxBlbDQOUvP-7CP2OSQV1ZRwtWeVXw,875
|
@@ -518,7 +518,7 @@ reconcile/test/test_github_repo_invites.py,sha256=WrPn5ROAoJYK0ihzlZcFR0V9Pu2HcM
|
|
518
518
|
reconcile/test/test_gitlab_housekeeping.py,sha256=ScD9Tgf9OOgGfAFfTy6Kn2222l2wH_A3gMRKdpvoWe0,10053
|
519
519
|
reconcile/test/test_gitlab_labeler.py,sha256=PmYXiU2g0_O5OTdMGPzdeqBAfat92U9bhjjfeyiGSmQ,4336
|
520
520
|
reconcile/test/test_gitlab_members.py,sha256=yjfQRUFG_F0kLYegax4_ec5VIBAnCPrvAgqMcN1GXzc,9985
|
521
|
-
reconcile/test/test_gitlab_permissions.py,sha256=
|
521
|
+
reconcile/test/test_gitlab_permissions.py,sha256=vPFEsdjyP-lO8pc2rN6acMns3Sjz9YJs16msbBR8DZc,2736
|
522
522
|
reconcile/test/test_instrumented_wrappers.py,sha256=CZzhnQH0c4i7-Rxjg7-0dfFMvVPegLHL46z5NHOOCwo,608
|
523
523
|
reconcile/test/test_integrations_manager.py,sha256=xpyQAVz57wAbovrcQzAeuyq8VzdItUyW2d2kp1WW_5c,38184
|
524
524
|
reconcile/test/test_jenkins_worker_fleets.py,sha256=o1jlT7OBBSgu0M3iI4xMdz_x6SciF7yhNBpLk5gTJfg,2361
|
@@ -674,7 +674,7 @@ reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,
|
|
674
674
|
reconcile/utils/git.py,sha256=JkpbUO10oBTtNHZ1IhjyG6dTOUizc7I5H0vm7NvDVNw,1409
|
675
675
|
reconcile/utils/git_secrets.py,sha256=y1rEhwA8DyDpBSAEuhMS7Y2X3mpxT2zQ4zyDFkhLe_g,1936
|
676
676
|
reconcile/utils/github_api.py,sha256=R8OvqyPdnRqvP-Efnv9RvIcbBlb4M0KC4RlbnJMD0Tg,2426
|
677
|
-
reconcile/utils/gitlab_api.py,sha256=
|
677
|
+
reconcile/utils/gitlab_api.py,sha256=WfhLhOp3-cOwgptiCNPhWja74Lo41ZIlJ6HFSWaIDRw,30512
|
678
678
|
reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
|
679
679
|
reconcile/utils/gql.py,sha256=NqxosFCInLy_dF_fcXnRYcZgsThedSyv_I3U8k2SLMg,14161
|
680
680
|
reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
|
@@ -865,8 +865,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
|
|
865
865
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
866
866
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
867
867
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
868
|
-
qontract_reconcile-0.10.
|
869
|
-
qontract_reconcile-0.10.
|
870
|
-
qontract_reconcile-0.10.
|
871
|
-
qontract_reconcile-0.10.
|
872
|
-
qontract_reconcile-0.10.
|
868
|
+
qontract_reconcile-0.10.1rc1031.dist-info/METADATA,sha256=IU8tp02udRNBkJ5CY9gEN_NBZcO87JMc_Kqe3JP4siY,2213
|
869
|
+
qontract_reconcile-0.10.1rc1031.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
870
|
+
qontract_reconcile-0.10.1rc1031.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
871
|
+
qontract_reconcile-0.10.1rc1031.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
872
|
+
qontract_reconcile-0.10.1rc1031.dist-info/RECORD,,
|
reconcile/gitlab_permissions.py
CHANGED
@@ -1,146 +1,21 @@
|
|
1
1
|
import itertools
|
2
2
|
import logging
|
3
|
-
from dataclasses import dataclass
|
4
3
|
from typing import Any
|
5
4
|
|
6
|
-
from gitlab.
|
7
|
-
GroupProject,
|
8
|
-
Project,
|
9
|
-
)
|
5
|
+
from gitlab.const import MAINTAINER_ACCESS
|
10
6
|
from sretoolbox.utils import threaded
|
11
7
|
|
12
8
|
from reconcile import queries
|
13
9
|
from reconcile.utils import batches
|
14
10
|
from reconcile.utils.defer import defer
|
15
|
-
from reconcile.utils.differ import diff_mappings
|
16
11
|
from reconcile.utils.gitlab_api import GitLabApi
|
17
12
|
from reconcile.utils.unleash import get_feature_toggle_state
|
18
13
|
|
19
14
|
QONTRACT_INTEGRATION = "gitlab-permissions"
|
20
15
|
APP_SRE_GROUP_NAME = "app-sre"
|
21
|
-
GROUP_ACCESS = "maintainer"
|
22
16
|
PAGE_SIZE = 100
|
23
17
|
|
24
18
|
|
25
|
-
@dataclass
|
26
|
-
class GroupSpec:
|
27
|
-
group_name: str
|
28
|
-
group_access_level: int
|
29
|
-
|
30
|
-
|
31
|
-
class GroupAccessLevelError(Exception):
|
32
|
-
pass
|
33
|
-
|
34
|
-
|
35
|
-
class GroupPermissionHandler:
|
36
|
-
def __init__(
|
37
|
-
self, gl: GitLabApi, group_name: str, access: str, dry_run: bool
|
38
|
-
) -> None:
|
39
|
-
self.gl = gl
|
40
|
-
self.dry_run = dry_run
|
41
|
-
self.access_level_string = access
|
42
|
-
self.access_level = self.gl.get_access_level(access)
|
43
|
-
self.group = self.gl.get_group(group_name)
|
44
|
-
|
45
|
-
def run(self, repos: list[str]) -> None:
|
46
|
-
# filter projects belonging to the same group and remove it from the state data
|
47
|
-
filtered_project_repos = self.filter_group_owned_projects(repos)
|
48
|
-
desired_state = {
|
49
|
-
project_repo_url: GroupSpec(self.group.name, self.access_level)
|
50
|
-
for project_repo_url in filtered_project_repos
|
51
|
-
}
|
52
|
-
# get all projects shared with group
|
53
|
-
shared_projects = self.gl.get_items(self.group.shared_projects.list)
|
54
|
-
current_state = {
|
55
|
-
project.web_url: self.extract_group_spec(project)
|
56
|
-
for project in shared_projects
|
57
|
-
}
|
58
|
-
self.reconcile(desired_state, current_state)
|
59
|
-
|
60
|
-
def filter_group_owned_projects(self, repos: list[str]) -> set[str]:
|
61
|
-
# get only the projects that are owned by group and its sub groups
|
62
|
-
query = {"with_shared": False, "include_subgroups": True}
|
63
|
-
group_owned_projects = self.gl.get_items(
|
64
|
-
self.group.projects.list, query_parameters=query
|
65
|
-
)
|
66
|
-
group_owned_repo_list = {project.web_url for project in group_owned_projects}
|
67
|
-
return set(repos) - group_owned_repo_list
|
68
|
-
|
69
|
-
def extract_group_spec(self, project: GroupProject) -> GroupSpec:
|
70
|
-
return next(
|
71
|
-
GroupSpec(
|
72
|
-
group_name=self.group.name,
|
73
|
-
group_access_level=group["group_access_level"],
|
74
|
-
)
|
75
|
-
for group in project.shared_with_groups
|
76
|
-
if group["group_id"] == self.group.id
|
77
|
-
)
|
78
|
-
|
79
|
-
def can_share_project(self, project: Project) -> bool:
|
80
|
-
# check if user have access greater or equal access to be shared with the group
|
81
|
-
user = project.members_all.get(id=self.gl.user.id)
|
82
|
-
return user.access_level >= self.access_level
|
83
|
-
|
84
|
-
def reconcile(
|
85
|
-
self,
|
86
|
-
desired_state: dict[str, GroupSpec],
|
87
|
-
current_state: dict[str, GroupSpec],
|
88
|
-
) -> None:
|
89
|
-
# get the diff data
|
90
|
-
diff_data = diff_mappings(
|
91
|
-
current=current_state,
|
92
|
-
desired=desired_state,
|
93
|
-
equal=lambda current, desired: current.group_access_level
|
94
|
-
== desired.group_access_level,
|
95
|
-
)
|
96
|
-
errors: list[Exception] = []
|
97
|
-
for repo in diff_data.add:
|
98
|
-
project = self.gl.get_project(repo)
|
99
|
-
if not self.can_share_project(project):
|
100
|
-
errors.append(
|
101
|
-
GroupAccessLevelError(
|
102
|
-
f"{repo} is not shared with {self.gl.user.username} as {self.access_level_string}"
|
103
|
-
)
|
104
|
-
)
|
105
|
-
continue
|
106
|
-
logging.info([
|
107
|
-
"share",
|
108
|
-
repo,
|
109
|
-
self.group.name,
|
110
|
-
self.access_level_string,
|
111
|
-
])
|
112
|
-
if not self.dry_run:
|
113
|
-
self.gl.share_project_with_group(
|
114
|
-
project=project,
|
115
|
-
group_id=self.group.id,
|
116
|
-
access_level=self.access_level,
|
117
|
-
)
|
118
|
-
for repo in diff_data.change:
|
119
|
-
project = self.gl.get_project(repo)
|
120
|
-
if not self.can_share_project(project):
|
121
|
-
errors.append(
|
122
|
-
GroupAccessLevelError(
|
123
|
-
f"{repo} is not shared with {self.gl.user.username} as {self.access_level_string}"
|
124
|
-
)
|
125
|
-
)
|
126
|
-
continue
|
127
|
-
logging.info([
|
128
|
-
"reshare",
|
129
|
-
repo,
|
130
|
-
self.group.name,
|
131
|
-
self.access_level_string,
|
132
|
-
])
|
133
|
-
if not self.dry_run:
|
134
|
-
self.gl.share_project_with_group(
|
135
|
-
project=project,
|
136
|
-
group_id=self.group.id,
|
137
|
-
access_level=self.access_level,
|
138
|
-
reshare=True,
|
139
|
-
)
|
140
|
-
if errors:
|
141
|
-
raise ExceptionGroup("Reconcile errors occurred", errors)
|
142
|
-
|
143
|
-
|
144
19
|
def get_members_to_add(repo, gl, app_sre):
|
145
20
|
maintainers = get_all_app_sre_maintainers(repo, gl, app_sre)
|
146
21
|
if maintainers is None:
|
@@ -181,10 +56,7 @@ def run(dry_run, thread_pool_size=10, defer=None):
|
|
181
56
|
default=False,
|
182
57
|
)
|
183
58
|
if share_with_group_enabled:
|
184
|
-
|
185
|
-
gl=gl, group_name=APP_SRE_GROUP_NAME, access=GROUP_ACCESS, dry_run=dry_run
|
186
|
-
)
|
187
|
-
group_permission_handler.run(repos=repos)
|
59
|
+
share_project_with_group(gl, repos, dry_run)
|
188
60
|
else:
|
189
61
|
share_project_with_group_members(gl, repos, thread_pool_size, dry_run)
|
190
62
|
|
@@ -203,6 +75,26 @@ def share_project_with_group_members(
|
|
203
75
|
gl.add_project_member(m["repo"], m["user"])
|
204
76
|
|
205
77
|
|
78
|
+
def share_project_with_group(gl: GitLabApi, repos: list[str], dry_run: bool) -> None:
|
79
|
+
# get repos not owned by app-sre
|
80
|
+
non_app_sre_project_repos = {repo for repo in repos if "/app-sre/" not in repo}
|
81
|
+
group_id, shared_projects = gl.get_group_id_and_shared_projects(APP_SRE_GROUP_NAME)
|
82
|
+
shared_project_repos = shared_projects.keys()
|
83
|
+
repos_to_share = non_app_sre_project_repos - shared_project_repos
|
84
|
+
repos_to_reshare = {
|
85
|
+
repo
|
86
|
+
for repo in non_app_sre_project_repos
|
87
|
+
if (group_data := shared_projects.get(repo))
|
88
|
+
and group_data["group_access_level"] < MAINTAINER_ACCESS
|
89
|
+
}
|
90
|
+
for repo in repos_to_share:
|
91
|
+
gl.share_project_with_group(repo_url=repo, group_id=group_id, dry_run=dry_run)
|
92
|
+
for repo in repos_to_reshare:
|
93
|
+
gl.share_project_with_group(
|
94
|
+
repo_url=repo, group_id=group_id, dry_run=dry_run, reshare=True
|
95
|
+
)
|
96
|
+
|
97
|
+
|
206
98
|
def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
|
207
99
|
instance = queries.get_gitlab_instance()
|
208
100
|
return {
|
@@ -1,17 +1,7 @@
|
|
1
1
|
from unittest.mock import MagicMock, create_autospec
|
2
2
|
|
3
3
|
import pytest
|
4
|
-
from gitlab.v4.objects import
|
5
|
-
CurrentUser,
|
6
|
-
Group,
|
7
|
-
GroupMember,
|
8
|
-
GroupProjectManager,
|
9
|
-
Project,
|
10
|
-
ProjectMember,
|
11
|
-
ProjectMemberAllManager,
|
12
|
-
SharedProject,
|
13
|
-
SharedProjectManager,
|
14
|
-
)
|
4
|
+
from gitlab.v4.objects import CurrentUser, GroupMember
|
15
5
|
from pytest_mock import MockerFixture
|
16
6
|
|
17
7
|
from reconcile import gitlab_permissions
|
@@ -33,7 +23,6 @@ def mocked_gl() -> MagicMock:
|
|
33
23
|
gl.server = "test_server"
|
34
24
|
gl.user = create_autospec(CurrentUser)
|
35
25
|
gl.user.username = "test_name"
|
36
|
-
gl.user.id = 1234
|
37
26
|
return gl
|
38
27
|
|
39
28
|
|
@@ -60,25 +49,13 @@ def test_run_share_with_group(
|
|
60
49
|
mocker.patch(
|
61
50
|
"reconcile.gitlab_permissions.get_feature_toggle_state"
|
62
51
|
).return_value = True
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
group.shared_projects = create_autospec(SharedProjectManager)
|
67
|
-
mocked_gl.get_items.side_effect = [
|
68
|
-
[],
|
69
|
-
[],
|
70
|
-
]
|
71
|
-
mocked_gl.get_group.return_value = group
|
72
|
-
mocked_gl.get_access_level.return_value = 40
|
73
|
-
project = create_autospec(Project, web_url="https://test.com")
|
74
|
-
project.members_all = create_autospec(ProjectMemberAllManager)
|
75
|
-
project.members_all.get.return_value = create_autospec(
|
76
|
-
ProjectMember, id=mocked_gl.user.id, access_level=40
|
52
|
+
mocked_gl.get_group_id_and_shared_projects.return_value = (
|
53
|
+
1234,
|
54
|
+
{"https://test.com": {"group_access_level": 30}},
|
77
55
|
)
|
78
|
-
mocked_gl.get_project.return_value = project
|
79
56
|
gitlab_permissions.run(False, thread_pool_size=1)
|
80
57
|
mocked_gl.share_project_with_group.assert_called_once_with(
|
81
|
-
|
58
|
+
repo_url="https://test-gitlab.com", group_id=1234, dry_run=False
|
82
59
|
)
|
83
60
|
|
84
61
|
|
@@ -89,76 +66,11 @@ def test_run_reshare_with_group(
|
|
89
66
|
mocker.patch(
|
90
67
|
"reconcile.gitlab_permissions.get_feature_toggle_state"
|
91
68
|
).return_value = True
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
group.shared_projects = create_autospec(SharedProjectManager)
|
96
|
-
mocked_gl.get_items.side_effect = [
|
97
|
-
[],
|
98
|
-
[
|
99
|
-
create_autospec(
|
100
|
-
SharedProject,
|
101
|
-
web_url="https://test-gitlab.com",
|
102
|
-
shared_with_groups=[
|
103
|
-
{
|
104
|
-
"group_access_level": 30,
|
105
|
-
"group_name": "app-sre",
|
106
|
-
"group_id": 1234,
|
107
|
-
}
|
108
|
-
],
|
109
|
-
)
|
110
|
-
],
|
111
|
-
]
|
112
|
-
mocked_gl.get_group.return_value = group
|
113
|
-
mocked_gl.get_access_level.return_value = 40
|
114
|
-
project = create_autospec(Project, web_url="https://test-gitlab.com")
|
115
|
-
project.members_all = create_autospec(ProjectMemberAllManager)
|
116
|
-
project.members_all.get.return_value = create_autospec(
|
117
|
-
ProjectMember, id=mocked_gl.user.id, access_level=40
|
69
|
+
mocked_gl.get_group_id_and_shared_projects.return_value = (
|
70
|
+
1234,
|
71
|
+
{"https://test-gitlab.com": {"group_access_level": 30}},
|
118
72
|
)
|
119
|
-
mocked_gl.get_project.return_value = project
|
120
73
|
gitlab_permissions.run(False, thread_pool_size=1)
|
121
74
|
mocked_gl.share_project_with_group.assert_called_once_with(
|
122
|
-
|
123
|
-
)
|
124
|
-
|
125
|
-
|
126
|
-
def test_run_share_with_group_failed(
|
127
|
-
mocked_queries: MagicMock, mocker: MockerFixture, mocked_gl: MagicMock
|
128
|
-
) -> None:
|
129
|
-
mocker.patch("reconcile.gitlab_permissions.GitLabApi").return_value = mocked_gl
|
130
|
-
mocker.patch(
|
131
|
-
"reconcile.gitlab_permissions.get_feature_toggle_state"
|
132
|
-
).return_value = True
|
133
|
-
group = create_autospec(Group, id=1234)
|
134
|
-
group.name = "app-sre"
|
135
|
-
group.projects = create_autospec(GroupProjectManager)
|
136
|
-
group.shared_projects = create_autospec(SharedProjectManager)
|
137
|
-
group.projects = create_autospec(GroupProjectManager)
|
138
|
-
group.shared_projects = create_autospec(SharedProjectManager)
|
139
|
-
mocked_gl.get_items.side_effect = [
|
140
|
-
[],
|
141
|
-
[
|
142
|
-
create_autospec(
|
143
|
-
SharedProject,
|
144
|
-
web_url="https://test-gitlab.com",
|
145
|
-
shared_with_groups=[
|
146
|
-
{
|
147
|
-
"group_access_level": 30,
|
148
|
-
"group_name": "app-sre",
|
149
|
-
"group_id": 134,
|
150
|
-
}
|
151
|
-
],
|
152
|
-
)
|
153
|
-
],
|
154
|
-
]
|
155
|
-
mocked_gl.get_group.return_value = group
|
156
|
-
mocked_gl.get_access_level.return_value = 40
|
157
|
-
project = create_autospec(Project)
|
158
|
-
project.members_all = create_autospec(ProjectMemberAllManager)
|
159
|
-
project.members_all.get.return_value = create_autospec(
|
160
|
-
ProjectMember, id=mocked_gl.user.id, access_level=10
|
75
|
+
repo_url="https://test-gitlab.com", group_id=1234, dry_run=False, reshare=True
|
161
76
|
)
|
162
|
-
mocked_gl.get_project.return_value = project
|
163
|
-
with pytest.raises(Exception):
|
164
|
-
gitlab_permissions.run(False, thread_pool_size=1)
|
reconcile/utils/gitlab_api.py
CHANGED
@@ -262,16 +262,51 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
262
262
|
|
263
263
|
def share_project_with_group(
|
264
264
|
self,
|
265
|
-
|
265
|
+
repo_url: str,
|
266
266
|
group_id: int,
|
267
|
-
|
267
|
+
dry_run: bool,
|
268
|
+
access: str = "maintainer",
|
268
269
|
reshare: bool = False,
|
269
270
|
) -> None:
|
270
|
-
|
271
|
+
project = self.get_project(repo_url)
|
272
|
+
if project is None:
|
273
|
+
return None
|
274
|
+
access_level = self.get_access_level(access)
|
275
|
+
# check if we have 'access_level' access so we can add the group with same role.
|
276
|
+
members = self.get_items(
|
277
|
+
project.members_all.list, query_parameters={"user_ids": self.user.id}
|
278
|
+
)
|
279
|
+
if not any(
|
280
|
+
self.user.id == member.id and member.access_level >= access_level
|
281
|
+
for member in members
|
282
|
+
):
|
283
|
+
logging.error(
|
284
|
+
"%s is not shared with %s as %s",
|
285
|
+
repo_url,
|
286
|
+
self.user.username,
|
287
|
+
access,
|
288
|
+
)
|
289
|
+
return None
|
290
|
+
logging.info(["add_group_as_maintainer", repo_url, group_id])
|
291
|
+
if not dry_run:
|
292
|
+
if reshare:
|
293
|
+
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
294
|
+
project.unshare(group_id)
|
271
295
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
272
|
-
project.
|
296
|
+
project.share(group_id, access_level)
|
297
|
+
|
298
|
+
def get_group_id_and_shared_projects(
|
299
|
+
self, group_name: str
|
300
|
+
) -> tuple[int, dict[str, Any]]:
|
273
301
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
274
|
-
|
302
|
+
group = self.gl.groups.get(group_name)
|
303
|
+
shared_projects = self.get_items(group.projects.list)
|
304
|
+
return group.id, {
|
305
|
+
project.web_url: shared_group
|
306
|
+
for project in shared_projects
|
307
|
+
for shared_group in project.shared_with_groups
|
308
|
+
if shared_group["group_id"] == group.id
|
309
|
+
}
|
275
310
|
|
276
311
|
@staticmethod
|
277
312
|
def _is_bot_username(username: str) -> bool:
|
@@ -379,9 +414,10 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
379
414
|
if access == "guest":
|
380
415
|
return GUEST_ACCESS
|
381
416
|
|
382
|
-
def
|
417
|
+
def get_group_id_and_projects(self, group_name: str) -> tuple[str, list[str]]:
|
383
418
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
384
|
-
|
419
|
+
group = self.gl.groups.get(group_name)
|
420
|
+
return group.id, [p.name for p in self.get_items(group.projects.list)]
|
385
421
|
|
386
422
|
def create_project(self, group_id, project):
|
387
423
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
{qontract_reconcile-0.10.1rc1030.dist-info → qontract_reconcile-0.10.1rc1031.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|