qontract-reconcile 0.10.1rc1038__py3-none-any.whl → 0.10.1rc1039__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.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/RECORD +8 -8
- reconcile/gitlab_permissions.py +130 -22
- reconcile/test/test_gitlab_permissions.py +97 -9
- reconcile/utils/gitlab_api.py +9 -40
- {qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.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.1rc1039
|
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.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.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=tEjbpr-QH03vZ4L6CuVJhkgA3oaB_gyJ7d58MTR4JJQ,7389
|
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
|
@@ -519,7 +519,7 @@ reconcile/test/test_github_repo_invites.py,sha256=WrPn5ROAoJYK0ihzlZcFR0V9Pu2HcM
|
|
519
519
|
reconcile/test/test_gitlab_housekeeping.py,sha256=ScD9Tgf9OOgGfAFfTy6Kn2222l2wH_A3gMRKdpvoWe0,10053
|
520
520
|
reconcile/test/test_gitlab_labeler.py,sha256=PmYXiU2g0_O5OTdMGPzdeqBAfat92U9bhjjfeyiGSmQ,4336
|
521
521
|
reconcile/test/test_gitlab_members.py,sha256=yjfQRUFG_F0kLYegax4_ec5VIBAnCPrvAgqMcN1GXzc,9985
|
522
|
-
reconcile/test/test_gitlab_permissions.py,sha256=
|
522
|
+
reconcile/test/test_gitlab_permissions.py,sha256=aMf5SUeVp-aQ1bWGQPQLYa85auzRlyfZVTWqyybJ6mo,5850
|
523
523
|
reconcile/test/test_instrumented_wrappers.py,sha256=CZzhnQH0c4i7-Rxjg7-0dfFMvVPegLHL46z5NHOOCwo,608
|
524
524
|
reconcile/test/test_integrations_manager.py,sha256=xpyQAVz57wAbovrcQzAeuyq8VzdItUyW2d2kp1WW_5c,38184
|
525
525
|
reconcile/test/test_jenkins_worker_fleets.py,sha256=o1jlT7OBBSgu0M3iI4xMdz_x6SciF7yhNBpLk5gTJfg,2361
|
@@ -676,7 +676,7 @@ reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,
|
|
676
676
|
reconcile/utils/git.py,sha256=JkpbUO10oBTtNHZ1IhjyG6dTOUizc7I5H0vm7NvDVNw,1409
|
677
677
|
reconcile/utils/git_secrets.py,sha256=y1rEhwA8DyDpBSAEuhMS7Y2X3mpxT2zQ4zyDFkhLe_g,1936
|
678
678
|
reconcile/utils/github_api.py,sha256=R8OvqyPdnRqvP-Efnv9RvIcbBlb4M0KC4RlbnJMD0Tg,2426
|
679
|
-
reconcile/utils/gitlab_api.py,sha256=
|
679
|
+
reconcile/utils/gitlab_api.py,sha256=C1nsHQKKybsmFdaG9vsItBjJm69ym4VWbqbKfAEf7oY,29305
|
680
680
|
reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
|
681
681
|
reconcile/utils/gql.py,sha256=C0thIm_k9MBldfqwHzyqtYZk9sIvMdm9IbbnXLGwjD8,14158
|
682
682
|
reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
|
@@ -867,8 +867,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
|
|
867
867
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
868
868
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
869
869
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
870
|
-
qontract_reconcile-0.10.
|
871
|
-
qontract_reconcile-0.10.
|
872
|
-
qontract_reconcile-0.10.
|
873
|
-
qontract_reconcile-0.10.
|
874
|
-
qontract_reconcile-0.10.
|
870
|
+
qontract_reconcile-0.10.1rc1039.dist-info/METADATA,sha256=Lg9_6kfW_Ccmxbo_ROXjegslf_Ji5aZZwd8RHgra-Xc,2213
|
871
|
+
qontract_reconcile-0.10.1rc1039.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
872
|
+
qontract_reconcile-0.10.1rc1039.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
873
|
+
qontract_reconcile-0.10.1rc1039.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
874
|
+
qontract_reconcile-0.10.1rc1039.dist-info/RECORD,,
|
reconcile/gitlab_permissions.py
CHANGED
@@ -1,21 +1,146 @@
|
|
1
1
|
import itertools
|
2
2
|
import logging
|
3
|
+
from dataclasses import dataclass
|
3
4
|
from typing import Any
|
4
5
|
|
5
|
-
from gitlab.
|
6
|
+
from gitlab.v4.objects import (
|
7
|
+
GroupProject,
|
8
|
+
Project,
|
9
|
+
)
|
6
10
|
from sretoolbox.utils import threaded
|
7
11
|
|
8
12
|
from reconcile import queries
|
9
13
|
from reconcile.utils import batches
|
10
14
|
from reconcile.utils.defer import defer
|
15
|
+
from reconcile.utils.differ import diff_mappings
|
11
16
|
from reconcile.utils.gitlab_api import GitLabApi
|
12
17
|
from reconcile.utils.unleash import get_feature_toggle_state
|
13
18
|
|
14
19
|
QONTRACT_INTEGRATION = "gitlab-permissions"
|
15
20
|
APP_SRE_GROUP_NAME = "app-sre"
|
21
|
+
GROUP_ACCESS = "maintainer"
|
16
22
|
PAGE_SIZE = 100
|
17
23
|
|
18
24
|
|
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
|
+
|
19
144
|
def get_members_to_add(repo, gl, app_sre):
|
20
145
|
maintainers = get_all_app_sre_maintainers(repo, gl, app_sre)
|
21
146
|
if maintainers is None:
|
@@ -56,7 +181,10 @@ def run(dry_run, thread_pool_size=10, defer=None):
|
|
56
181
|
default=False,
|
57
182
|
)
|
58
183
|
if share_with_group_enabled:
|
59
|
-
|
184
|
+
group_permission_handler = GroupPermissionHandler(
|
185
|
+
gl=gl, group_name=APP_SRE_GROUP_NAME, access=GROUP_ACCESS, dry_run=dry_run
|
186
|
+
)
|
187
|
+
group_permission_handler.run(repos=repos)
|
60
188
|
else:
|
61
189
|
share_project_with_group_members(gl, repos, thread_pool_size, dry_run)
|
62
190
|
|
@@ -75,26 +203,6 @@ def share_project_with_group_members(
|
|
75
203
|
gl.add_project_member(m["repo"], m["user"])
|
76
204
|
|
77
205
|
|
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
|
-
|
98
206
|
def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
|
99
207
|
instance = queries.get_gitlab_instance()
|
100
208
|
return {
|
@@ -1,7 +1,17 @@
|
|
1
1
|
from unittest.mock import MagicMock, create_autospec
|
2
2
|
|
3
3
|
import pytest
|
4
|
-
from gitlab.v4.objects import
|
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
|
+
)
|
5
15
|
from pytest_mock import MockerFixture
|
6
16
|
|
7
17
|
from reconcile import gitlab_permissions
|
@@ -23,6 +33,7 @@ def mocked_gl() -> MagicMock:
|
|
23
33
|
gl.server = "test_server"
|
24
34
|
gl.user = create_autospec(CurrentUser)
|
25
35
|
gl.user.username = "test_name"
|
36
|
+
gl.user.id = 1234
|
26
37
|
return gl
|
27
38
|
|
28
39
|
|
@@ -49,13 +60,25 @@ def test_run_share_with_group(
|
|
49
60
|
mocker.patch(
|
50
61
|
"reconcile.gitlab_permissions.get_feature_toggle_state"
|
51
62
|
).return_value = True
|
52
|
-
|
53
|
-
|
54
|
-
|
63
|
+
group = create_autospec(Group, id=1234)
|
64
|
+
group.name = "app-sre"
|
65
|
+
group.projects = create_autospec(GroupProjectManager)
|
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
|
55
77
|
)
|
78
|
+
mocked_gl.get_project.return_value = project
|
56
79
|
gitlab_permissions.run(False, thread_pool_size=1)
|
57
80
|
mocked_gl.share_project_with_group.assert_called_once_with(
|
58
|
-
|
81
|
+
project, group_id=1234, access_level=40
|
59
82
|
)
|
60
83
|
|
61
84
|
|
@@ -66,11 +89,76 @@ def test_run_reshare_with_group(
|
|
66
89
|
mocker.patch(
|
67
90
|
"reconcile.gitlab_permissions.get_feature_toggle_state"
|
68
91
|
).return_value = True
|
69
|
-
|
70
|
-
|
71
|
-
|
92
|
+
group = create_autospec(Group, id=1234)
|
93
|
+
group.name = "app-sre"
|
94
|
+
group.projects = create_autospec(GroupProjectManager)
|
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
|
72
118
|
)
|
119
|
+
mocked_gl.get_project.return_value = project
|
73
120
|
gitlab_permissions.run(False, thread_pool_size=1)
|
74
121
|
mocked_gl.share_project_with_group.assert_called_once_with(
|
75
|
-
|
122
|
+
project=project, group_id=1234, access_level=40, reshare=True
|
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
|
76
161
|
)
|
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,51 +262,16 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
262
262
|
|
263
263
|
def share_project_with_group(
|
264
264
|
self,
|
265
|
-
|
265
|
+
project: Project,
|
266
266
|
group_id: int,
|
267
|
-
|
268
|
-
access: str = "maintainer",
|
267
|
+
access_level: int,
|
269
268
|
reshare: bool = False,
|
270
269
|
) -> None:
|
271
|
-
|
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)
|
270
|
+
if reshare:
|
295
271
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
296
|
-
project.
|
297
|
-
|
298
|
-
def get_group_id_and_shared_projects(
|
299
|
-
self, group_name: str
|
300
|
-
) -> tuple[int, dict[str, Any]]:
|
272
|
+
project.unshare(group_id)
|
301
273
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
302
|
-
|
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
|
-
}
|
274
|
+
project.share(group_id, access_level)
|
310
275
|
|
311
276
|
@staticmethod
|
312
277
|
def _is_bot_username(username: str) -> bool:
|
@@ -419,6 +384,10 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
419
384
|
group = self.gl.groups.get(group_name)
|
420
385
|
return group.id, [p.name for p in self.get_items(group.projects.list)]
|
421
386
|
|
387
|
+
def get_group(self, group_name: str) -> Group:
|
388
|
+
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
389
|
+
return self.gl.groups.get(group_name)
|
390
|
+
|
422
391
|
def create_project(self, group_id, project):
|
423
392
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
424
393
|
self.gl.projects.create({"name": project, "namespace_id": group_id})
|
{qontract_reconcile-0.10.1rc1038.dist-info → qontract_reconcile-0.10.1rc1039.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|