argus-alm 0.12.10__py3-none-any.whl → 0.13.0__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.
- argus/client/base.py +1 -1
- argus/client/driver_matrix_tests/cli.py +2 -2
- argus/client/driver_matrix_tests/client.py +1 -1
- argus/client/generic/cli.py +2 -2
- argus/client/sct/client.py +3 -3
- argus/client/sirenada/client.py +1 -1
- {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/METADATA +2 -4
- argus_alm-0.13.0.dist-info/RECORD +20 -0
- argus/backend/.gitkeep +0 -0
- argus/backend/cli.py +0 -41
- argus/backend/controller/__init__.py +0 -0
- argus/backend/controller/admin.py +0 -20
- argus/backend/controller/admin_api.py +0 -354
- argus/backend/controller/api.py +0 -529
- argus/backend/controller/auth.py +0 -67
- argus/backend/controller/client_api.py +0 -108
- argus/backend/controller/main.py +0 -274
- argus/backend/controller/notification_api.py +0 -72
- argus/backend/controller/notifications.py +0 -13
- argus/backend/controller/team.py +0 -126
- argus/backend/controller/team_ui.py +0 -18
- argus/backend/controller/testrun_api.py +0 -482
- argus/backend/controller/view_api.py +0 -162
- argus/backend/db.py +0 -100
- argus/backend/error_handlers.py +0 -21
- argus/backend/events/event_processors.py +0 -34
- argus/backend/models/__init__.py +0 -0
- argus/backend/models/result.py +0 -138
- argus/backend/models/web.py +0 -389
- argus/backend/plugins/__init__.py +0 -0
- argus/backend/plugins/core.py +0 -225
- argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
- argus/backend/plugins/driver_matrix_tests/model.py +0 -421
- argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
- argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
- argus/backend/plugins/driver_matrix_tests/service.py +0 -60
- argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
- argus/backend/plugins/generic/model.py +0 -79
- argus/backend/plugins/generic/plugin.py +0 -16
- argus/backend/plugins/generic/types.py +0 -13
- argus/backend/plugins/loader.py +0 -40
- argus/backend/plugins/sct/controller.py +0 -185
- argus/backend/plugins/sct/plugin.py +0 -38
- argus/backend/plugins/sct/resource_setup.py +0 -178
- argus/backend/plugins/sct/service.py +0 -491
- argus/backend/plugins/sct/testrun.py +0 -272
- argus/backend/plugins/sct/udt.py +0 -101
- argus/backend/plugins/sirenada/model.py +0 -113
- argus/backend/plugins/sirenada/plugin.py +0 -17
- argus/backend/service/admin.py +0 -27
- argus/backend/service/argus_service.py +0 -688
- argus/backend/service/build_system_monitor.py +0 -188
- argus/backend/service/client_service.py +0 -122
- argus/backend/service/event_service.py +0 -18
- argus/backend/service/jenkins_service.py +0 -240
- argus/backend/service/notification_manager.py +0 -150
- argus/backend/service/release_manager.py +0 -230
- argus/backend/service/results_service.py +0 -317
- argus/backend/service/stats.py +0 -540
- argus/backend/service/team_manager_service.py +0 -83
- argus/backend/service/testrun.py +0 -559
- argus/backend/service/user.py +0 -307
- argus/backend/service/views.py +0 -258
- argus/backend/template_filters.py +0 -27
- argus/backend/tests/__init__.py +0 -0
- argus/backend/tests/argus_web.test.yaml +0 -39
- argus/backend/tests/conftest.py +0 -44
- argus/backend/tests/results_service/__init__.py +0 -0
- argus/backend/tests/results_service/test_best_results.py +0 -70
- argus/backend/util/common.py +0 -65
- argus/backend/util/config.py +0 -38
- argus/backend/util/encoders.py +0 -41
- argus/backend/util/logsetup.py +0 -81
- argus/backend/util/module_loaders.py +0 -30
- argus/backend/util/send_email.py +0 -91
- argus/client/generic_result_old.py +0 -143
- argus/db/.gitkeep +0 -0
- argus/db/argus_json.py +0 -14
- argus/db/cloud_types.py +0 -125
- argus/db/config.py +0 -135
- argus/db/db_types.py +0 -139
- argus/db/interface.py +0 -370
- argus/db/testrun.py +0 -740
- argus/db/utils.py +0 -15
- argus_alm-0.12.10.dist-info/RECORD +0 -96
- /argus/{backend → common}/__init__.py +0 -0
- /argus/{backend/util → common}/enums.py +0 -0
- /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
- /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
- {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/LICENSE +0 -0
- {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/WHEEL +0 -0
- {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,688 +0,0 @@
|
|
|
1
|
-
from math import ceil
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
import subprocess
|
|
4
|
-
import json
|
|
5
|
-
import logging
|
|
6
|
-
import datetime
|
|
7
|
-
from types import NoneType
|
|
8
|
-
from uuid import UUID
|
|
9
|
-
from cassandra.util import uuid_from_time # pylint: disable=no-name-in-module
|
|
10
|
-
from flask import current_app
|
|
11
|
-
from argus.backend.db import ScyllaCluster
|
|
12
|
-
from argus.backend.plugins.loader import AVAILABLE_PLUGINS, all_plugin_models
|
|
13
|
-
from argus.backend.plugins.sct.testrun import SCTTestRun
|
|
14
|
-
from argus.backend.service.notification_manager import NotificationManagerService
|
|
15
|
-
from argus.backend.models.web import (
|
|
16
|
-
ArgusRelease,
|
|
17
|
-
ArgusGroup,
|
|
18
|
-
ArgusTest,
|
|
19
|
-
ArgusSchedule,
|
|
20
|
-
ArgusScheduleAssignee,
|
|
21
|
-
ArgusScheduleGroup,
|
|
22
|
-
ArgusScheduleTest,
|
|
23
|
-
ArgusTestRunComment,
|
|
24
|
-
ArgusEvent,
|
|
25
|
-
ReleasePlannerComment,
|
|
26
|
-
User,
|
|
27
|
-
)
|
|
28
|
-
from argus.backend.events.event_processors import EVENT_PROCESSORS
|
|
29
|
-
from argus.backend.service.testrun import TestRunService
|
|
30
|
-
from argus.backend.util.common import chunk
|
|
31
|
-
|
|
32
|
-
LOGGER = logging.getLogger(__name__)
|
|
33
|
-
|
|
34
|
-
@dataclass(init=True, frozen=True)
|
|
35
|
-
class ScheduleUpdateRequest:
|
|
36
|
-
release_id: UUID
|
|
37
|
-
schedule_id: UUID
|
|
38
|
-
assignee: UUID
|
|
39
|
-
new_tests: list[UUID]
|
|
40
|
-
old_tests: list[UUID]
|
|
41
|
-
comments: dict[UUID, str]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class ArgusService:
|
|
46
|
-
# pylint: disable=no-self-use,too-many-arguments,too-many-instance-attributes,too-many-locals, too-many-public-methods
|
|
47
|
-
def __init__(self, database_session=None):
|
|
48
|
-
self.session = database_session if database_session else ScyllaCluster.get_session()
|
|
49
|
-
self.database = ScyllaCluster.get()
|
|
50
|
-
self.notification_manager = NotificationManagerService()
|
|
51
|
-
self.github_headers = {
|
|
52
|
-
"Accept": "application/vnd.github.v3+json",
|
|
53
|
-
"Authorization": f"token {current_app.config['GITHUB_ACCESS_TOKEN']}"
|
|
54
|
-
}
|
|
55
|
-
self.build_id_and_url_statement = self.database.prepare(
|
|
56
|
-
f"SELECT build_id, build_job_url, test_id FROM {SCTTestRun.table_name()} WHERE id = ?"
|
|
57
|
-
) # TODO: transfer to PluginModelBase
|
|
58
|
-
self.scylla_versions_by_release = self.database.prepare(
|
|
59
|
-
f"SELECT scylla_version FROM {SCTTestRun.table_name()} WHERE release_id = ?"
|
|
60
|
-
) # TODO: Moved to PluginModelBase
|
|
61
|
-
|
|
62
|
-
def get_version(self) -> str:
|
|
63
|
-
try:
|
|
64
|
-
proc = subprocess.run(["git", "rev-parse", "HEAD"], check=True, capture_output=True)
|
|
65
|
-
except subprocess.CalledProcessError:
|
|
66
|
-
proc = None
|
|
67
|
-
if proc:
|
|
68
|
-
return proc.stdout.decode(encoding="utf-8").strip()
|
|
69
|
-
else:
|
|
70
|
-
try:
|
|
71
|
-
with open("./.argus_version", 'rt', encoding="utf-8") as version_file:
|
|
72
|
-
version = version_file.read().strip()
|
|
73
|
-
return version
|
|
74
|
-
except FileNotFoundError:
|
|
75
|
-
return "version_unknown"
|
|
76
|
-
|
|
77
|
-
def create_release(self, payload: dict) -> dict:
|
|
78
|
-
response = {}
|
|
79
|
-
for release_name in payload:
|
|
80
|
-
try:
|
|
81
|
-
ArgusRelease.get(name=release_name)
|
|
82
|
-
response[release_name] = {
|
|
83
|
-
"status": "error",
|
|
84
|
-
"message": f"Release {release_name} already exists"
|
|
85
|
-
}
|
|
86
|
-
continue
|
|
87
|
-
except ArgusRelease.DoesNotExist:
|
|
88
|
-
pass
|
|
89
|
-
|
|
90
|
-
new_release = ArgusRelease()
|
|
91
|
-
new_release.name = release_name
|
|
92
|
-
new_release.save()
|
|
93
|
-
response[release_name] = {}
|
|
94
|
-
response[release_name]["groups"] = self.create_groups(
|
|
95
|
-
groups=payload[release_name]["groups"],
|
|
96
|
-
parent_release_id=new_release.id
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
return response
|
|
100
|
-
|
|
101
|
-
def create_groups(self, groups: dict, parent_release_id) -> dict:
|
|
102
|
-
response = {}
|
|
103
|
-
for group_name, group_definition in groups.items():
|
|
104
|
-
new_group = ArgusGroup()
|
|
105
|
-
new_group.release_id = parent_release_id
|
|
106
|
-
new_group.name = group_name
|
|
107
|
-
new_group.pretty_name = group_definition.get("pretty_name")
|
|
108
|
-
new_group.save()
|
|
109
|
-
response[group_name] = {}
|
|
110
|
-
response[group_name]["status"] = "created"
|
|
111
|
-
response[group_name]["tests"] = self.create_tests(
|
|
112
|
-
tests=group_definition.get("tests", []),
|
|
113
|
-
parent_group_id=new_group.id,
|
|
114
|
-
parent_release_id=parent_release_id
|
|
115
|
-
)
|
|
116
|
-
return response
|
|
117
|
-
|
|
118
|
-
def create_tests(self, tests: dict, parent_group_id: UUID, parent_release_id: UUID) -> dict:
|
|
119
|
-
response = {}
|
|
120
|
-
|
|
121
|
-
for test_name in tests:
|
|
122
|
-
new_test = ArgusTest()
|
|
123
|
-
new_test.release_id = parent_release_id
|
|
124
|
-
new_test.group_id = parent_group_id
|
|
125
|
-
new_test.name = test_name
|
|
126
|
-
new_test.save()
|
|
127
|
-
response[test_name] = "created"
|
|
128
|
-
|
|
129
|
-
return response
|
|
130
|
-
|
|
131
|
-
def get_comment(self, comment_id: UUID) -> ArgusTestRunComment | None:
|
|
132
|
-
try:
|
|
133
|
-
return ArgusTestRunComment.get(id=comment_id)
|
|
134
|
-
except ArgusTestRunComment.DoesNotExist:
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
def get_releases(self):
|
|
138
|
-
releases = list(ArgusRelease.all())
|
|
139
|
-
releases = sorted(releases, key=lambda r: r.name)
|
|
140
|
-
releases = sorted(releases, key=lambda r: r.dormant)
|
|
141
|
-
return releases
|
|
142
|
-
|
|
143
|
-
def get_groups(self, release_id: UUID) -> list[ArgusGroup]:
|
|
144
|
-
groups = list(ArgusGroup.filter(release_id=release_id).all())
|
|
145
|
-
return sorted(groups, key=lambda g: g.pretty_name if g.pretty_name else g.name)
|
|
146
|
-
|
|
147
|
-
def get_groups_for_release(self, release: ArgusRelease):
|
|
148
|
-
groups = ArgusGroup.filter(release_id=release.id).all()
|
|
149
|
-
return sorted(groups, key=lambda g: g.pretty_name if g.pretty_name else g.name)
|
|
150
|
-
|
|
151
|
-
def get_tests(self, group_id: UUID) -> list[ArgusTest]:
|
|
152
|
-
return list(ArgusTest.filter(group_id=group_id).all())
|
|
153
|
-
|
|
154
|
-
def get_test_info(self, test_id: UUID) -> dict:
|
|
155
|
-
test = ArgusTest.get(id=test_id)
|
|
156
|
-
group = ArgusGroup.get(id=test.group_id)
|
|
157
|
-
release = ArgusRelease.get(id=test.release_id)
|
|
158
|
-
return {
|
|
159
|
-
"test": dict(test.items()),
|
|
160
|
-
"group": dict(group.items()),
|
|
161
|
-
"release": dict(release.items()),
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
def get_data_for_release_dashboard(self, release_name: str):
|
|
165
|
-
release = ArgusRelease.get(name=release_name)
|
|
166
|
-
release_groups = ArgusGroup.filter(release_id=release.id).all()
|
|
167
|
-
release_tests = ArgusTest.filter(release_id=release.id).all()
|
|
168
|
-
|
|
169
|
-
return release, release_groups, release_tests
|
|
170
|
-
|
|
171
|
-
def get_distinct_release_versions(self, release_id: UUID | str) -> list[str]:
|
|
172
|
-
release_id = UUID(release_id) if isinstance(release_id, str) else release_id
|
|
173
|
-
release = ArgusRelease.get(id=release_id)
|
|
174
|
-
unique_versions = {ver for plugin in all_plugin_models()
|
|
175
|
-
for ver in plugin.get_distinct_product_versions(release=release)}
|
|
176
|
-
|
|
177
|
-
return sorted(list(unique_versions), reverse=True)
|
|
178
|
-
|
|
179
|
-
def poll_test_runs(self, test_id: UUID, additional_runs: list[UUID], limit: int = 10):
|
|
180
|
-
test: ArgusTest = ArgusTest.get(id=test_id)
|
|
181
|
-
|
|
182
|
-
rows: list[SCTTestRun] = list(SCTTestRun.filter(build_id=test.build_system_id).all().limit(limit))
|
|
183
|
-
|
|
184
|
-
rows_ids = [row.id for row in rows]
|
|
185
|
-
|
|
186
|
-
for run_id in additional_runs:
|
|
187
|
-
if run_id not in rows_ids:
|
|
188
|
-
row: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
189
|
-
rows.append(row)
|
|
190
|
-
|
|
191
|
-
for row in rows:
|
|
192
|
-
try:
|
|
193
|
-
setattr(row, "build_number", int(row["build_job_url"].rstrip("/").split("/")[-1]))
|
|
194
|
-
except ValueError:
|
|
195
|
-
setattr(row, "build_number", -1)
|
|
196
|
-
|
|
197
|
-
return sorted(rows, reverse=True, key=lambda r: r.build_number)
|
|
198
|
-
|
|
199
|
-
def poll_test_runs_single(self, runs: list[UUID]):
|
|
200
|
-
rows: list[SCTTestRun] = []
|
|
201
|
-
for run_id in runs:
|
|
202
|
-
try:
|
|
203
|
-
row: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
204
|
-
rows.append(row)
|
|
205
|
-
except SCTTestRun.DoesNotExist:
|
|
206
|
-
pass
|
|
207
|
-
|
|
208
|
-
response = {str(row.id): row for row in rows}
|
|
209
|
-
return response
|
|
210
|
-
|
|
211
|
-
def fetch_release_activity(self, release_name: str) -> dict:
|
|
212
|
-
response = {}
|
|
213
|
-
release = ArgusRelease.get(name=release_name)
|
|
214
|
-
all_events = ArgusEvent.filter(release_id=release.id).all()
|
|
215
|
-
all_events = sorted(all_events, key=lambda ev: ev.created_at)
|
|
216
|
-
response["release_id"] = release.id
|
|
217
|
-
response["raw_events"] = [dict(event.items()) for event in all_events]
|
|
218
|
-
response["events"] = {str(event.id): EVENT_PROCESSORS.get(
|
|
219
|
-
event.kind)(json.loads(event.body)) for event in all_events}
|
|
220
|
-
return response
|
|
221
|
-
|
|
222
|
-
def assign_runs_for_scheduled_test(self, schedule: ArgusSchedule, test_id: UUID, new_assignee: UUID):
|
|
223
|
-
test: ArgusTest = ArgusTest.get(id=test_id)
|
|
224
|
-
affected_rows: list[SCTTestRun] = list(SCTTestRun.filter(
|
|
225
|
-
build_id=test.build_system_id,
|
|
226
|
-
start_time__gte=schedule.period_start,
|
|
227
|
-
start_time__lte=schedule.period_end
|
|
228
|
-
).all()
|
|
229
|
-
)
|
|
230
|
-
for row in affected_rows:
|
|
231
|
-
if row.assignee != new_assignee:
|
|
232
|
-
row.assignee = new_assignee
|
|
233
|
-
row.save()
|
|
234
|
-
|
|
235
|
-
def assign_runs_for_scheduled_group(self, schedule: ArgusSchedule, group_id: UUID, new_assignee: UUID):
|
|
236
|
-
tests = ArgusTest.filter(group_id=group_id).all()
|
|
237
|
-
build_ids = [test.build_system_id for test in tests]
|
|
238
|
-
affected_rows: list[SCTTestRun] = list(SCTTestRun.filter(
|
|
239
|
-
start_time__gte=schedule.period_start,
|
|
240
|
-
start_time__lte=schedule.period_end
|
|
241
|
-
).all()
|
|
242
|
-
)
|
|
243
|
-
for row in affected_rows:
|
|
244
|
-
if row.build_id in build_ids and row.assignee != new_assignee:
|
|
245
|
-
row.assignee = new_assignee
|
|
246
|
-
row.save()
|
|
247
|
-
|
|
248
|
-
def submit_new_schedule(self, release: str | UUID, start_time: str, end_time: str, tests: list[str | UUID],
|
|
249
|
-
groups: list[str | UUID], assignees: list[str | UUID], tag: str, comments: dict[str, str] | None, group_ids: dict[str, str] | None) -> dict:
|
|
250
|
-
release = UUID(release) if isinstance(release, str) else release
|
|
251
|
-
if len(assignees) == 0:
|
|
252
|
-
raise Exception("Assignees not specified in the new schedule")
|
|
253
|
-
|
|
254
|
-
if len(tests) == 0 and len(groups) == 0:
|
|
255
|
-
raise Exception("Schedule does not contain scheduled objects")
|
|
256
|
-
|
|
257
|
-
schedule = ArgusSchedule()
|
|
258
|
-
schedule.release_id = release
|
|
259
|
-
schedule.period_start = datetime.datetime.fromisoformat(start_time)
|
|
260
|
-
schedule.period_end = datetime.datetime.fromisoformat(end_time)
|
|
261
|
-
schedule.id = uuid_from_time(schedule.period_start)
|
|
262
|
-
schedule.tag = tag
|
|
263
|
-
schedule.save()
|
|
264
|
-
|
|
265
|
-
response = dict(schedule.items())
|
|
266
|
-
response["assignees"] = []
|
|
267
|
-
response["tests"] = []
|
|
268
|
-
response["groups"] = []
|
|
269
|
-
|
|
270
|
-
for test_id in tests:
|
|
271
|
-
test_entity = ArgusScheduleTest()
|
|
272
|
-
test_entity.id = uuid_from_time(schedule.period_start)
|
|
273
|
-
test_entity.schedule_id = schedule.id
|
|
274
|
-
test_entity.test_id = UUID(test_id) if isinstance(test_id, str) else test_id
|
|
275
|
-
test_entity.release_id = release
|
|
276
|
-
test_entity.save()
|
|
277
|
-
self.assign_runs_for_scheduled_test(schedule, test_entity.test_id, UUID(assignees[0]))
|
|
278
|
-
response["tests"].append(test_id)
|
|
279
|
-
|
|
280
|
-
for group_id in groups:
|
|
281
|
-
group_entity = ArgusScheduleGroup()
|
|
282
|
-
group_entity.id = uuid_from_time(schedule.period_start)
|
|
283
|
-
group_entity.schedule_id = schedule.id
|
|
284
|
-
group_entity.group_id = UUID(group_id) if isinstance(group_id, str) else group_id
|
|
285
|
-
group_entity.release_id = release
|
|
286
|
-
group_entity.save()
|
|
287
|
-
self.assign_runs_for_scheduled_group(schedule, group_entity.group_id, UUID(assignees[0]))
|
|
288
|
-
response["groups"].append(group_id)
|
|
289
|
-
|
|
290
|
-
for assignee_id in assignees:
|
|
291
|
-
assignee_entity = ArgusScheduleAssignee()
|
|
292
|
-
assignee_entity.id = uuid_from_time(schedule.period_start)
|
|
293
|
-
assignee_entity.schedule_id = schedule.id
|
|
294
|
-
assignee_entity.assignee = UUID(assignee_id) if isinstance(assignee_id, str) else assignee_id
|
|
295
|
-
assignee_entity.release_id = release
|
|
296
|
-
assignee_entity.save()
|
|
297
|
-
response["assignees"].append(assignee_id)
|
|
298
|
-
|
|
299
|
-
if comments:
|
|
300
|
-
for test_id, new_comment in comments.items():
|
|
301
|
-
try:
|
|
302
|
-
comment = ReleasePlannerComment.get(release=release, group=group_ids[test_id], test=test_id)
|
|
303
|
-
except ReleasePlannerComment.DoesNotExist:
|
|
304
|
-
comment = ReleasePlannerComment()
|
|
305
|
-
comment.release = release
|
|
306
|
-
comment.group = group_ids[test_id]
|
|
307
|
-
comment.test = test_id
|
|
308
|
-
|
|
309
|
-
comment.comment = new_comment
|
|
310
|
-
comment.save()
|
|
311
|
-
|
|
312
|
-
return response
|
|
313
|
-
|
|
314
|
-
def get_schedules_for_release(self, release_id: str | UUID) -> dict:
|
|
315
|
-
"""
|
|
316
|
-
{
|
|
317
|
-
"release": "hex-uuid"
|
|
318
|
-
}
|
|
319
|
-
"""
|
|
320
|
-
release_id = UUID(release_id) if isinstance(release_id, str) else release_id
|
|
321
|
-
release: ArgusRelease = ArgusRelease.get(id=release_id)
|
|
322
|
-
if release.perpetual:
|
|
323
|
-
today = datetime.datetime.utcnow()
|
|
324
|
-
six_months_ago = today - datetime.timedelta(days=180)
|
|
325
|
-
uuid_six_months = uuid_from_time(six_months_ago)
|
|
326
|
-
schedules = ArgusSchedule.filter(release_id=release_id, id__gte=uuid_six_months).all()
|
|
327
|
-
else:
|
|
328
|
-
schedules = ArgusSchedule.filter(release_id=release_id).all()
|
|
329
|
-
response = {
|
|
330
|
-
"schedules": []
|
|
331
|
-
}
|
|
332
|
-
for schedule in schedules:
|
|
333
|
-
serialized_schedule = dict(schedule.items())
|
|
334
|
-
tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
|
|
335
|
-
serialized_schedule["tests"] = [test.test_id for test in tests]
|
|
336
|
-
groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
|
|
337
|
-
serialized_schedule["groups"] = [group.group_id for group in groups]
|
|
338
|
-
assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
|
|
339
|
-
serialized_schedule["assignees"] = [assignee.assignee for assignee in assignees]
|
|
340
|
-
response["schedules"].append(serialized_schedule)
|
|
341
|
-
|
|
342
|
-
return response
|
|
343
|
-
|
|
344
|
-
def update_schedule_assignees(self, payload: dict) -> dict:
|
|
345
|
-
"""
|
|
346
|
-
{
|
|
347
|
-
"releaseId": "hex-uuid",
|
|
348
|
-
"scheduleId": "hex-uuid",
|
|
349
|
-
}
|
|
350
|
-
"""
|
|
351
|
-
release_id = payload.get("releaseId")
|
|
352
|
-
if not release_id:
|
|
353
|
-
raise Exception("Release name not specified in the request")
|
|
354
|
-
|
|
355
|
-
schedule_id = payload.get("scheduleId")
|
|
356
|
-
if not schedule_id:
|
|
357
|
-
raise Exception("No schedule Id provided")
|
|
358
|
-
|
|
359
|
-
assignees = payload.get("newAssignees")
|
|
360
|
-
if not assignees:
|
|
361
|
-
raise Exception("No assignees provided")
|
|
362
|
-
|
|
363
|
-
release = ArgusRelease.get(id=release_id)
|
|
364
|
-
schedule = ArgusSchedule.get(release_id=release.id, id=schedule_id)
|
|
365
|
-
schedule_tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
|
|
366
|
-
schedule_groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
|
|
367
|
-
for test in schedule_tests:
|
|
368
|
-
self.assign_runs_for_scheduled_test(schedule, test.test_id, UUID(assignees[0]))
|
|
369
|
-
|
|
370
|
-
for group in schedule_groups:
|
|
371
|
-
self.assign_runs_for_scheduled_group(schedule, group.group_id, UUID(assignees[0]))
|
|
372
|
-
|
|
373
|
-
old_assignees = list(ArgusScheduleAssignee.filter(schedule_id=schedule.id).all())
|
|
374
|
-
for new_assignee in assignees:
|
|
375
|
-
assignee = ArgusScheduleAssignee()
|
|
376
|
-
assignee.release_id = release.id
|
|
377
|
-
assignee.schedule_id = schedule.id
|
|
378
|
-
assignee.assignee = UUID(new_assignee)
|
|
379
|
-
assignee.save()
|
|
380
|
-
|
|
381
|
-
for old_assignee in old_assignees:
|
|
382
|
-
old_assignee.delete()
|
|
383
|
-
|
|
384
|
-
response = {
|
|
385
|
-
"scheduleId": schedule_id,
|
|
386
|
-
"status": "changed assignees",
|
|
387
|
-
"newAssignees": assignees,
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return response
|
|
391
|
-
|
|
392
|
-
def update_schedule_comment(self, payload: dict) -> dict:
|
|
393
|
-
new_comment = payload.get("newComment")
|
|
394
|
-
release_id = payload.get("releaseId")
|
|
395
|
-
group_id = payload.get("groupId")
|
|
396
|
-
test_id = payload.get("testId")
|
|
397
|
-
|
|
398
|
-
if not release_id:
|
|
399
|
-
raise Exception("No release provided")
|
|
400
|
-
if not group_id:
|
|
401
|
-
raise Exception("No group provided")
|
|
402
|
-
if not test_id:
|
|
403
|
-
raise Exception("No test provided")
|
|
404
|
-
|
|
405
|
-
if isinstance(new_comment, NoneType):
|
|
406
|
-
raise Exception("No comment provided in the body of request")
|
|
407
|
-
|
|
408
|
-
try:
|
|
409
|
-
comment = ReleasePlannerComment.get(release=release_id, group=group_id, test=test_id)
|
|
410
|
-
except ReleasePlannerComment.DoesNotExist:
|
|
411
|
-
comment = ReleasePlannerComment()
|
|
412
|
-
comment.release = release_id
|
|
413
|
-
comment.group = group_id
|
|
414
|
-
comment.test = test_id
|
|
415
|
-
|
|
416
|
-
comment.comment = new_comment
|
|
417
|
-
comment.save()
|
|
418
|
-
|
|
419
|
-
return {
|
|
420
|
-
"releaseId": release_id,
|
|
421
|
-
"groupId": group_id,
|
|
422
|
-
"testId": test_id,
|
|
423
|
-
"newComment": new_comment,
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
def update_schedule(self, release_id: UUID | str, schedule_id: UUID | str, old_tests: list[UUID | str], new_tests: list[UUID | str], comments: dict[str, str], assignee: UUID | str):
|
|
427
|
-
schedule: ArgusSchedule = ArgusSchedule.get(release_id=release_id, id=schedule_id)
|
|
428
|
-
new_tests: set[UUID] = {UUID(id) for id in new_tests}
|
|
429
|
-
old_tests: set[UUID] = {UUID(id) for id in old_tests}
|
|
430
|
-
|
|
431
|
-
all_test_ids = old_tests.union(new_tests)
|
|
432
|
-
tests = []
|
|
433
|
-
for batch in chunk(all_test_ids):
|
|
434
|
-
tests.extend(ArgusTest.filter(id__in=batch).all())
|
|
435
|
-
tests_by_id: dict[UUID, ArgusTest] = { test.id: test for test in tests }
|
|
436
|
-
|
|
437
|
-
all_scheduled_tests: list[ArgusScheduleTest] = list(ArgusScheduleTest.filter(schedule_id=schedule_id).all())
|
|
438
|
-
tests_to_remove = all_test_ids.difference(new_tests)
|
|
439
|
-
for scheduled_test in all_scheduled_tests:
|
|
440
|
-
if scheduled_test.test_id in tests_to_remove:
|
|
441
|
-
test = tests_by_id.get(scheduled_test.test_id)
|
|
442
|
-
scheduled_test.delete()
|
|
443
|
-
if test:
|
|
444
|
-
self.update_schedule_comment({"newComment": "", "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
|
|
445
|
-
|
|
446
|
-
tests_to_add = new_tests.difference(old_tests)
|
|
447
|
-
for test_id in tests_to_add:
|
|
448
|
-
entity = ArgusScheduleTest()
|
|
449
|
-
entity.id = uuid_from_time(schedule.period_start)
|
|
450
|
-
entity.schedule_id = schedule.id
|
|
451
|
-
entity.test_id = UUID(test_id) if isinstance(test_id, str) else test_id
|
|
452
|
-
entity.release_id = release_id
|
|
453
|
-
entity.save()
|
|
454
|
-
self.assign_runs_for_scheduled_test(schedule, entity.test_id, assignee)
|
|
455
|
-
|
|
456
|
-
for test_id, comment in comments.items():
|
|
457
|
-
test = tests_by_id.get(UUID(test_id))
|
|
458
|
-
if test:
|
|
459
|
-
self.update_schedule_comment({"newComment": comment, "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
|
|
460
|
-
|
|
461
|
-
schedule_assignee: ArgusScheduleAssignee = ArgusScheduleAssignee.get(schedule_id=schedule_id)
|
|
462
|
-
new_assignee = ArgusScheduleAssignee()
|
|
463
|
-
new_assignee.assignee = assignee
|
|
464
|
-
new_assignee.release_id = schedule_assignee.release_id
|
|
465
|
-
new_assignee.schedule_id = schedule_assignee.schedule_id
|
|
466
|
-
new_assignee.save()
|
|
467
|
-
schedule_assignee.delete()
|
|
468
|
-
|
|
469
|
-
return True
|
|
470
|
-
|
|
471
|
-
def delete_schedule(self, payload: dict) -> dict:
|
|
472
|
-
"""
|
|
473
|
-
{
|
|
474
|
-
"release": hex-uuid,
|
|
475
|
-
"schedule_id": uuid1,
|
|
476
|
-
"deleteComments": bool
|
|
477
|
-
}
|
|
478
|
-
"""
|
|
479
|
-
release_id = payload.get("releaseId")
|
|
480
|
-
if not release_id:
|
|
481
|
-
raise Exception("Release name not specified in the request")
|
|
482
|
-
|
|
483
|
-
schedule_id = payload.get("scheduleId")
|
|
484
|
-
if not schedule_id:
|
|
485
|
-
raise Exception("Schedule id not specified in the request")
|
|
486
|
-
|
|
487
|
-
delete_comments = payload.get("deleteComments", False)
|
|
488
|
-
|
|
489
|
-
release = ArgusRelease.get(id=release_id)
|
|
490
|
-
schedule = ArgusSchedule.get(release_id=release.id, id=schedule_id)
|
|
491
|
-
tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
|
|
492
|
-
groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
|
|
493
|
-
assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
|
|
494
|
-
|
|
495
|
-
full_schedule = dict(schedule)
|
|
496
|
-
full_schedule["tests"] = [test.test_id for test in tests]
|
|
497
|
-
full_schedule["groups"] = [group.group_id for group in groups]
|
|
498
|
-
full_schedule["assignees"] = [assignee.assignee for assignee in assignees]
|
|
499
|
-
|
|
500
|
-
if len(assignees) > 0:
|
|
501
|
-
try:
|
|
502
|
-
schedule_user = User.get(id=assignees[0].assignee)
|
|
503
|
-
except User.DoesNotExist:
|
|
504
|
-
schedule_user = User()
|
|
505
|
-
schedule_user.id = assignees[0].assignee
|
|
506
|
-
LOGGER.warning("Deleting orphaned user assignments")
|
|
507
|
-
service = TestRunService()
|
|
508
|
-
|
|
509
|
-
for model in all_plugin_models():
|
|
510
|
-
for run in model.get_jobs_assigned_to_user(schedule_user.id):
|
|
511
|
-
if run["release_id"] != release.id:
|
|
512
|
-
continue
|
|
513
|
-
if run["test_id"] not in full_schedule["tests"]:
|
|
514
|
-
continue
|
|
515
|
-
if schedule.period_start < run["start_time"] < schedule.period_end:
|
|
516
|
-
service.change_run_assignee(test_id=run["test_id"], run_id=run["id"], new_assignee=None)
|
|
517
|
-
|
|
518
|
-
for entities in [tests, groups, assignees]:
|
|
519
|
-
for entity in entities:
|
|
520
|
-
entity.delete()
|
|
521
|
-
|
|
522
|
-
schedule.delete()
|
|
523
|
-
|
|
524
|
-
if delete_comments:
|
|
525
|
-
tests = []
|
|
526
|
-
for batch in chunk(full_schedule["tests"]):
|
|
527
|
-
tests.extend(ArgusTest.filter(id__in=batch).all())
|
|
528
|
-
|
|
529
|
-
for test in tests:
|
|
530
|
-
self.update_schedule_comment({"newComment": "", "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
|
|
531
|
-
|
|
532
|
-
return {
|
|
533
|
-
"releaseId": release.id,
|
|
534
|
-
"scheduleId": schedule_id,
|
|
535
|
-
"result": "deleted"
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
def get_planner_data(self, release_id: UUID | str) -> dict:
|
|
539
|
-
|
|
540
|
-
release = ArgusRelease.get(id=release_id)
|
|
541
|
-
release_comments = list(ReleasePlannerComment.filter(release=release.id).all())
|
|
542
|
-
groups = ArgusGroup.filter(release_id=release.id).all()
|
|
543
|
-
groups_by_group_id = {str(group.id): dict(group.items()) for group in groups if group.enabled}
|
|
544
|
-
tests = ArgusTest.filter(release_id=release.id).all()
|
|
545
|
-
tests = [dict(t.items()) for t in tests if t.enabled]
|
|
546
|
-
tests_by_group = {}
|
|
547
|
-
for test in tests:
|
|
548
|
-
group = groups_by_group_id.get(str(test["group_id"]))
|
|
549
|
-
if not group:
|
|
550
|
-
continue
|
|
551
|
-
test["group_name"] = group["name"]
|
|
552
|
-
test["pretty_group_name"] = groups_by_group_id[str(test["group_id"])]["pretty_name"]
|
|
553
|
-
try:
|
|
554
|
-
comment = next(filter(lambda c: c.test == test["id"], release_comments))
|
|
555
|
-
except StopIteration:
|
|
556
|
-
comment = None
|
|
557
|
-
test["comment"] = comment.comment if comment else ""
|
|
558
|
-
group_tests = tests_by_group.get(test["group_name"], [])
|
|
559
|
-
group_tests.append(test)
|
|
560
|
-
tests_by_group[test["group_name"]] = group_tests
|
|
561
|
-
|
|
562
|
-
response = {
|
|
563
|
-
"release": dict(release.items()),
|
|
564
|
-
"groups": groups_by_group_id,
|
|
565
|
-
"tests": tests,
|
|
566
|
-
"tests_by_group": tests_by_group,
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return response
|
|
570
|
-
|
|
571
|
-
def _batch_get_schedules_from_ids(self, release_id: UUID, schedule_ids: list[UUID]) -> list[ArgusSchedule]:
|
|
572
|
-
schedules = []
|
|
573
|
-
step_size = 90
|
|
574
|
-
for step in range(0, ceil(len(schedule_ids) / step_size)):
|
|
575
|
-
start_pos = step*step_size
|
|
576
|
-
next_slice = schedule_ids[start_pos:start_pos+step_size]
|
|
577
|
-
schedules.extend(ArgusSchedule.filter(release_id=release_id, id__in=next_slice).all())
|
|
578
|
-
return schedules
|
|
579
|
-
|
|
580
|
-
def get_groups_assignees(self, release_id: UUID | str):
|
|
581
|
-
release_id = UUID(release_id) if isinstance(release_id, str) else release_id
|
|
582
|
-
release = ArgusRelease.get(id=release_id)
|
|
583
|
-
|
|
584
|
-
groups = ArgusGroup.filter(release_id=release_id).all()
|
|
585
|
-
group_ids = [group.id for group in groups if group.enabled]
|
|
586
|
-
|
|
587
|
-
schedule_ids = set()
|
|
588
|
-
group_schedules =[]
|
|
589
|
-
step_size = 90
|
|
590
|
-
|
|
591
|
-
for step in range(0, ceil(len(group_ids) / step_size)):
|
|
592
|
-
start_pos = step*step_size
|
|
593
|
-
next_slice = group_ids[start_pos:start_pos+step_size]
|
|
594
|
-
group_batch = list(ArgusScheduleGroup.filter(release_id=release.id, group_id__in=next_slice).all())
|
|
595
|
-
group_schedules.extend(group_batch)
|
|
596
|
-
batch_ids = {schedule.schedule_id for schedule in group_batch}
|
|
597
|
-
schedule_ids = schedule_ids.union(batch_ids)
|
|
598
|
-
|
|
599
|
-
schedules = self._batch_get_schedules_from_ids(release.id, list(schedule_ids))
|
|
600
|
-
|
|
601
|
-
valid_schedules = schedules
|
|
602
|
-
if release.perpetual:
|
|
603
|
-
today = datetime.datetime.utcnow()
|
|
604
|
-
valid_schedules = [s for s in schedules if s.period_start <= today <= s.period_end]
|
|
605
|
-
|
|
606
|
-
response = {}
|
|
607
|
-
for schedule in valid_schedules:
|
|
608
|
-
assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
|
|
609
|
-
assignees_uuids = [assignee.assignee for assignee in assignees]
|
|
610
|
-
schedule_groups = filter(lambda g: g.schedule_id == schedule.id, group_schedules)
|
|
611
|
-
groups = {str(group.group_id): assignees_uuids for group in schedule_groups}
|
|
612
|
-
response = {**groups, **response}
|
|
613
|
-
|
|
614
|
-
return response
|
|
615
|
-
|
|
616
|
-
def get_tests_assignees(self, group_id: UUID | str):
|
|
617
|
-
group_id = UUID(group_id) if isinstance(group_id, str) else group_id
|
|
618
|
-
group = ArgusGroup.get(id=group_id)
|
|
619
|
-
|
|
620
|
-
release = ArgusRelease.get(id=group.release_id)
|
|
621
|
-
tests = ArgusTest.filter(group_id=group_id).all()
|
|
622
|
-
|
|
623
|
-
test_ids = [test.id for test in tests if test.enabled]
|
|
624
|
-
|
|
625
|
-
schedule_ids = set()
|
|
626
|
-
test_schedules = []
|
|
627
|
-
step_size = 90
|
|
628
|
-
|
|
629
|
-
for step in range(0, ceil(len(test_ids) / step_size)):
|
|
630
|
-
start_pos = step*step_size
|
|
631
|
-
next_slice = test_ids[start_pos:start_pos+step_size]
|
|
632
|
-
test_batch = ArgusScheduleTest.filter(release_id=release.id, test_id__in=next_slice).all()
|
|
633
|
-
test_schedules.extend(test_batch)
|
|
634
|
-
batch_ids = {schedule.schedule_id for schedule in test_batch}
|
|
635
|
-
schedule_ids = schedule_ids.union(batch_ids)
|
|
636
|
-
|
|
637
|
-
schedules = self._batch_get_schedules_from_ids(release.id, list(schedule_ids))
|
|
638
|
-
|
|
639
|
-
if release.perpetual:
|
|
640
|
-
today = datetime.datetime.utcnow()
|
|
641
|
-
schedules = list(filter(lambda s: s.period_start <= today <= s.period_end, schedules))
|
|
642
|
-
|
|
643
|
-
response = {}
|
|
644
|
-
for schedule in schedules:
|
|
645
|
-
assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
|
|
646
|
-
assignees_uuids = [assignee.assignee for assignee in assignees]
|
|
647
|
-
schedule_tests = filter(lambda t: t.schedule_id == schedule.id, test_schedules)
|
|
648
|
-
tests = {str(test.test_id): assignees_uuids for test in schedule_tests}
|
|
649
|
-
response = {**tests, **response}
|
|
650
|
-
|
|
651
|
-
return response
|
|
652
|
-
|
|
653
|
-
def get_jobs_for_user(self, user: User):
|
|
654
|
-
today = datetime.datetime.now()
|
|
655
|
-
validity_period = today - datetime.timedelta(days=current_app.config.get("JOB_VALIDITY_PERIOD_DAYS", 30))
|
|
656
|
-
for plugin in all_plugin_models():
|
|
657
|
-
for run in plugin.get_jobs_assigned_to_user(user_id=user.id):
|
|
658
|
-
if run["start_time"] >= validity_period:
|
|
659
|
-
yield run
|
|
660
|
-
|
|
661
|
-
def get_schedules_for_user(self, user: User) -> list[dict]:
|
|
662
|
-
all_assigned_schedules = ArgusScheduleAssignee.filter(assignee=user.id).all()
|
|
663
|
-
schedule_keys = [(schedule_assignee.release_id, schedule_assignee.schedule_id)
|
|
664
|
-
for schedule_assignee in all_assigned_schedules]
|
|
665
|
-
schedules = []
|
|
666
|
-
today = datetime.datetime.utcnow()
|
|
667
|
-
for release_id, schedule_id in schedule_keys:
|
|
668
|
-
try:
|
|
669
|
-
schedule = dict(ArgusSchedule.get(release_id=release_id, id=schedule_id).items())
|
|
670
|
-
except ArgusSchedule.DoesNotExist:
|
|
671
|
-
continue
|
|
672
|
-
if schedule["period_start"] <= today <= schedule["period_end"]:
|
|
673
|
-
tests = ArgusScheduleTest.filter(schedule_id=schedule_id).all()
|
|
674
|
-
schedule["tests"] = [test.test_id for test in tests]
|
|
675
|
-
groups = ArgusScheduleGroup.filter(schedule_id=schedule_id).all()
|
|
676
|
-
schedule["groups"] = [group.group_id for group in groups]
|
|
677
|
-
assignees = ArgusScheduleAssignee.filter(schedule_id=schedule_id).all()
|
|
678
|
-
schedule["assignees"] = [assignee.assignee for assignee in assignees]
|
|
679
|
-
schedules.append(schedule)
|
|
680
|
-
|
|
681
|
-
return schedules
|
|
682
|
-
|
|
683
|
-
def get_planner_comment_by_test(self, test_id):
|
|
684
|
-
try:
|
|
685
|
-
test = ArgusTest.get(id=test_id)
|
|
686
|
-
return ReleasePlannerComment.get(test=test.id, release=test.release_id, group=test.group_id).comment
|
|
687
|
-
except ReleasePlannerComment.DoesNotExist:
|
|
688
|
-
return ""
|