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.
Files changed (92) hide show
  1. argus/client/base.py +1 -1
  2. argus/client/driver_matrix_tests/cli.py +2 -2
  3. argus/client/driver_matrix_tests/client.py +1 -1
  4. argus/client/generic/cli.py +2 -2
  5. argus/client/sct/client.py +3 -3
  6. argus/client/sirenada/client.py +1 -1
  7. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/METADATA +2 -4
  8. argus_alm-0.13.0.dist-info/RECORD +20 -0
  9. argus/backend/.gitkeep +0 -0
  10. argus/backend/cli.py +0 -41
  11. argus/backend/controller/__init__.py +0 -0
  12. argus/backend/controller/admin.py +0 -20
  13. argus/backend/controller/admin_api.py +0 -354
  14. argus/backend/controller/api.py +0 -529
  15. argus/backend/controller/auth.py +0 -67
  16. argus/backend/controller/client_api.py +0 -108
  17. argus/backend/controller/main.py +0 -274
  18. argus/backend/controller/notification_api.py +0 -72
  19. argus/backend/controller/notifications.py +0 -13
  20. argus/backend/controller/team.py +0 -126
  21. argus/backend/controller/team_ui.py +0 -18
  22. argus/backend/controller/testrun_api.py +0 -482
  23. argus/backend/controller/view_api.py +0 -162
  24. argus/backend/db.py +0 -100
  25. argus/backend/error_handlers.py +0 -21
  26. argus/backend/events/event_processors.py +0 -34
  27. argus/backend/models/__init__.py +0 -0
  28. argus/backend/models/result.py +0 -138
  29. argus/backend/models/web.py +0 -389
  30. argus/backend/plugins/__init__.py +0 -0
  31. argus/backend/plugins/core.py +0 -225
  32. argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  33. argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  34. argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
  35. argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
  36. argus/backend/plugins/driver_matrix_tests/service.py +0 -60
  37. argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
  38. argus/backend/plugins/generic/model.py +0 -79
  39. argus/backend/plugins/generic/plugin.py +0 -16
  40. argus/backend/plugins/generic/types.py +0 -13
  41. argus/backend/plugins/loader.py +0 -40
  42. argus/backend/plugins/sct/controller.py +0 -185
  43. argus/backend/plugins/sct/plugin.py +0 -38
  44. argus/backend/plugins/sct/resource_setup.py +0 -178
  45. argus/backend/plugins/sct/service.py +0 -491
  46. argus/backend/plugins/sct/testrun.py +0 -272
  47. argus/backend/plugins/sct/udt.py +0 -101
  48. argus/backend/plugins/sirenada/model.py +0 -113
  49. argus/backend/plugins/sirenada/plugin.py +0 -17
  50. argus/backend/service/admin.py +0 -27
  51. argus/backend/service/argus_service.py +0 -688
  52. argus/backend/service/build_system_monitor.py +0 -188
  53. argus/backend/service/client_service.py +0 -122
  54. argus/backend/service/event_service.py +0 -18
  55. argus/backend/service/jenkins_service.py +0 -240
  56. argus/backend/service/notification_manager.py +0 -150
  57. argus/backend/service/release_manager.py +0 -230
  58. argus/backend/service/results_service.py +0 -317
  59. argus/backend/service/stats.py +0 -540
  60. argus/backend/service/team_manager_service.py +0 -83
  61. argus/backend/service/testrun.py +0 -559
  62. argus/backend/service/user.py +0 -307
  63. argus/backend/service/views.py +0 -258
  64. argus/backend/template_filters.py +0 -27
  65. argus/backend/tests/__init__.py +0 -0
  66. argus/backend/tests/argus_web.test.yaml +0 -39
  67. argus/backend/tests/conftest.py +0 -44
  68. argus/backend/tests/results_service/__init__.py +0 -0
  69. argus/backend/tests/results_service/test_best_results.py +0 -70
  70. argus/backend/util/common.py +0 -65
  71. argus/backend/util/config.py +0 -38
  72. argus/backend/util/encoders.py +0 -41
  73. argus/backend/util/logsetup.py +0 -81
  74. argus/backend/util/module_loaders.py +0 -30
  75. argus/backend/util/send_email.py +0 -91
  76. argus/client/generic_result_old.py +0 -143
  77. argus/db/.gitkeep +0 -0
  78. argus/db/argus_json.py +0 -14
  79. argus/db/cloud_types.py +0 -125
  80. argus/db/config.py +0 -135
  81. argus/db/db_types.py +0 -139
  82. argus/db/interface.py +0 -370
  83. argus/db/testrun.py +0 -740
  84. argus/db/utils.py +0 -15
  85. argus_alm-0.12.10.dist-info/RECORD +0 -96
  86. /argus/{backend → common}/__init__.py +0 -0
  87. /argus/{backend/util → common}/enums.py +0 -0
  88. /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
  89. /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
  90. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/LICENSE +0 -0
  91. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/WHEEL +0 -0
  92. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,559 +0,0 @@
1
- from collections import defaultdict
2
- from datetime import datetime, timedelta
3
- from functools import reduce
4
- import json
5
- import logging
6
- import re
7
- import time
8
- from typing import Any
9
- from uuid import UUID
10
-
11
- import requests
12
- from flask import g
13
- from cassandra.query import BatchStatement, ConsistencyLevel
14
- from cassandra.cqlengine.query import BatchQuery
15
- from argus.backend.db import ScyllaCluster
16
- from argus.backend.models.result import ArgusGenericResultMetadata
17
-
18
- from argus.backend.models.web import (
19
- ArgusEvent,
20
- ArgusEventTypes,
21
- ArgusGithubIssue,
22
- ArgusGroup,
23
- ArgusNotificationSourceTypes,
24
- ArgusNotificationTypes,
25
- ArgusRelease,
26
- ArgusTest,
27
- ArgusTestRunComment,
28
- ArgusUserView,
29
- User,
30
- UserOauthToken,
31
- )
32
-
33
- from argus.backend.plugins.core import PluginInfoBase, PluginModelBase
34
-
35
- from argus.backend.plugins.loader import AVAILABLE_PLUGINS
36
- from argus.backend.events.event_processors import EVENT_PROCESSORS
37
- from argus.backend.service.event_service import EventService
38
- from argus.backend.service.notification_manager import NotificationManagerService
39
- from argus.backend.service.stats import ComparableTestStatus
40
- from argus.backend.util.common import chunk, get_build_number, strip_html_tags
41
- from argus.backend.util.enums import TestInvestigationStatus, TestStatus
42
-
43
- LOGGER = logging.getLogger(__name__)
44
-
45
-
46
- class TestRunServiceException(Exception):
47
- pass
48
-
49
-
50
- class TestRunService:
51
- ASSIGNEE_PLACEHOLDER = "none-none-none"
52
-
53
- RE_MENTION = r"@[A-Za-z\d](?:[A-Za-z\d]|-(?=[A-Za-z\d])){0,38}"
54
-
55
- plugins = AVAILABLE_PLUGINS
56
- github_headers = {
57
- "Accept": "application/vnd.github.v3+json",
58
- }
59
-
60
- def __init__(self) -> None:
61
- self.notification_manager = NotificationManagerService()
62
-
63
- def get_plugin(self, plugin_name: str) -> PluginInfoBase | None:
64
- return self.plugins.get(plugin_name)
65
-
66
- def get_run(self, run_type: str, run_id: UUID) -> PluginModelBase:
67
- plugin = self.plugins.get(run_type)
68
- if plugin:
69
- try:
70
- return plugin.model.get(id=run_id)
71
- except plugin.model.DoesNotExist:
72
- return None
73
-
74
- def get_runs_by_test_id(self, test_id: UUID, additional_runs: list[UUID], limit: int = 10):
75
- test: ArgusTest = ArgusTest.get(id=test_id)
76
- plugin = self.get_plugin(plugin_name=test.plugin_name)
77
- if not plugin:
78
- return []
79
-
80
- last_runs: list[dict] = plugin.model.get_run_meta_by_build_id(build_id=test.build_system_id, limit=limit)
81
- last_runs_ids = [run["id"] for run in last_runs]
82
- for added_run in additional_runs:
83
- if added_run not in last_runs_ids:
84
- last_runs.extend(plugin.model.get_run_meta_by_run_id(run_id=added_run))
85
-
86
- for row in last_runs:
87
- row["build_number"] = get_build_number(build_job_url=row["build_job_url"])
88
-
89
- last_runs = sorted(last_runs, reverse=True, key=lambda run: (run["build_number"], ComparableTestStatus(TestStatus(run["status"]))))
90
-
91
- return last_runs
92
-
93
- def get_runs_by_id(self, test_id: UUID, runs: list[UUID]): # FIXME: Not needed, use get_run and individual polling
94
- # This is a batch request.
95
- test = ArgusTest.get(id=test_id)
96
- plugin = self.get_plugin(plugin_name=test.plugin_name)
97
- polled_runs: list[PluginModelBase] = []
98
- for run_id in runs:
99
- try:
100
- run: PluginModelBase = plugin.model.get(id=run_id)
101
- polled_runs.append(run)
102
- except plugin.model.DoesNotExist:
103
- pass
104
-
105
- response = {str(run.id): run for run in polled_runs}
106
- return response
107
-
108
- def change_run_status(self, test_id: UUID, run_id: UUID, new_status: TestStatus):
109
- try:
110
- test = ArgusTest.get(id=test_id)
111
- except ArgusTest.DoesNotExist as exc:
112
- raise TestRunServiceException("Test entity does not exist for provided test_id", test_id) from exc
113
- plugin = self.get_plugin(plugin_name=test.plugin_name)
114
- run: PluginModelBase = plugin.model.get(id=run_id)
115
- old_status = run.status
116
- run.status = new_status.value
117
- run.save()
118
-
119
- EventService.create_run_event(
120
- kind=ArgusEventTypes.TestRunStatusChanged,
121
- body={
122
- "message": "Status was changed from {old_status} to {new_status} by {username}",
123
- "old_status": old_status,
124
- "new_status": new_status.value,
125
- "username": g.user.username
126
- },
127
- user_id=g.user.id,
128
- run_id=run.id,
129
- release_id=test.release_id,
130
- group_id=test.group_id,
131
- test_id=test.id
132
- )
133
-
134
- return {
135
- "test_run_id": run.id,
136
- "status": new_status
137
- }
138
-
139
- def change_run_investigation_status(self, test_id: UUID, run_id: UUID, new_status: TestInvestigationStatus):
140
- test = ArgusTest.get(id=test_id)
141
- plugin = self.get_plugin(plugin_name=test.plugin_name)
142
- run: PluginModelBase = plugin.model.get(id=run_id)
143
- old_status = run.investigation_status
144
- run.investigation_status = new_status.value
145
- run.save()
146
-
147
- EventService.create_run_event(
148
- kind=ArgusEventTypes.TestRunStatusChanged,
149
- body={
150
- "message": "Investigation status was changed from {old_status} to {new_status} by {username}",
151
- "old_status": old_status,
152
- "new_status": new_status.value,
153
- "username": g.user.username
154
- },
155
- user_id=g.user.id,
156
- run_id=run.id,
157
- release_id=test.release_id,
158
- group_id=test.group_id,
159
- test_id=test.id
160
- )
161
-
162
- return {
163
- "test_run_id": run.id,
164
- "investigation_status": new_status
165
- }
166
-
167
- def change_run_assignee(self, test_id: UUID, run_id: UUID, new_assignee: UUID | None):
168
- test = ArgusTest.get(id=test_id)
169
- plugin = self.get_plugin(plugin_name=test.plugin_name)
170
- if not plugin:
171
- return {
172
- "test_run_id": run.id,
173
- "assignee": None
174
- }
175
-
176
- run: PluginModelBase = plugin.model.get(id=run_id)
177
- old_assignee = run.assignee
178
- run.assignee = new_assignee
179
- run.save()
180
-
181
- if new_assignee:
182
- new_assignee_user = User.get(id=new_assignee)
183
- else:
184
- new_assignee_user = None
185
- if old_assignee:
186
- try:
187
- old_assignee_user = User.get(id=old_assignee)
188
- except User.DoesNotExist:
189
- LOGGER.warning("Non existent assignee was present on the run %s for test %s: %s",
190
- run_id, test_id, old_assignee)
191
- old_assignee = None
192
- EventService.create_run_event(
193
- kind=ArgusEventTypes.AssigneeChanged,
194
- body={
195
- "message": "Assignee was changed from \"{old_user}\" to \"{new_user}\" by {username}",
196
- "old_user": old_assignee_user.username if old_assignee else "None",
197
- "new_user": new_assignee_user.username if new_assignee else "None",
198
- "username": g.user.username
199
- },
200
- user_id=g.user.id,
201
- run_id=run.id,
202
- release_id=test.release_id,
203
- group_id=test.group_id,
204
- test_id=test.id
205
- )
206
- if new_assignee_user.id != g.user.id:
207
- self.notification_manager.send_notification(
208
- receiver=new_assignee_user.id,
209
- sender=g.user.id,
210
- notification_type=ArgusNotificationTypes.AssigneeChange,
211
- source_type=ArgusNotificationSourceTypes.TestRun,
212
- source_id=run.id,
213
- source_message=str(run.test_id),
214
- content_params={
215
- "username": g.user.username,
216
- "run_id": run.id,
217
- "test_id": test.id,
218
- "build_id": run.build_id,
219
- "build_number": get_build_number(run.build_job_url),
220
- }
221
- )
222
- return {
223
- "test_run_id": run.id,
224
- "assignee": str(new_assignee_user.id) if new_assignee_user else None
225
- }
226
-
227
- def get_run_comment(self, comment_id: UUID):
228
- try:
229
- return ArgusTestRunComment.get(id=comment_id)
230
- except ArgusTestRunComment.DoesNotExist:
231
- return None
232
-
233
- def get_run_comments(self, run_id: UUID):
234
- return sorted(ArgusTestRunComment.filter(test_run_id=run_id).all(), key=lambda c: c.posted_at)
235
-
236
- def post_run_comment(self, test_id: UUID, run_id: UUID, message: str, reactions: dict, mentions: list[str]):
237
- message_stripped = strip_html_tags(message)
238
-
239
- mentions = set(mentions)
240
- for potential_mention in re.findall(self.RE_MENTION, message_stripped):
241
- if user := User.exists_by_name(potential_mention.lstrip("@")):
242
- mentions.add(user) if user.id != g.user.id else None
243
-
244
- test: ArgusTest = ArgusTest.get(id=test_id)
245
- plugin = self.get_plugin(test.plugin_name)
246
- release: ArgusRelease = ArgusRelease.get(id=test.release_id)
247
- comment = ArgusTestRunComment()
248
- comment.test_id = test.id
249
- comment.message = message_stripped
250
- comment.reactions = reactions
251
- comment.mentions = [m.id for m in mentions]
252
- comment.test_run_id = run_id
253
- comment.release_id = release.id
254
- comment.user_id = g.user.id
255
- comment.posted_at = time.time()
256
- comment.save()
257
-
258
- run: PluginModelBase = plugin.model.get(id=run_id)
259
- build_number = get_build_number(build_job_url=run.build_job_url)
260
- for mention in mentions:
261
- params = {
262
- "username": g.user.username,
263
- "run_id": comment.test_run_id,
264
- "test_id": test.id,
265
- "build_id": run.build_id,
266
- "build_number": build_number,
267
- }
268
- self.notification_manager.send_notification(
269
- receiver=mention.id,
270
- sender=comment.user_id,
271
- notification_type=ArgusNotificationTypes.Mention,
272
- source_type=ArgusNotificationSourceTypes.Comment,
273
- source_id=comment.id,
274
- source_message=comment.message,
275
- content_params=params
276
- )
277
-
278
- EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentPosted, body={
279
- "message": "A comment was posted by {username}",
280
- "username": g.user.username
281
- }, user_id=g.user.id, run_id=run_id, release_id=release.id, test_id=test.id)
282
-
283
- return self.get_run_comments(run_id=run_id)
284
-
285
- def delete_run_comment(self, comment_id: UUID, test_id: UUID, run_id: UUID):
286
- comment: ArgusTestRunComment = ArgusTestRunComment.get(id=comment_id)
287
- if comment.user_id != g.user.id:
288
- raise Exception("Unable to delete other user comments")
289
- comment.delete()
290
-
291
- EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentDeleted, body={
292
- "message": "A comment was deleted by {username}",
293
- "username": g.user.username
294
- }, user_id=g.user.id, run_id=run_id, release_id=comment.release_id, test_id=test_id)
295
-
296
- return self.get_run_comments(run_id=run_id)
297
-
298
- def update_run_comment(self, comment_id: UUID, test_id: UUID, run_id: UUID, message: str, mentions: list[str], reactions: dict):
299
- comment: ArgusTestRunComment = ArgusTestRunComment.get(id=comment_id)
300
- if comment.user_id != g.user.id:
301
- raise Exception("Unable to edit other user comments")
302
- comment.message = strip_html_tags(message)
303
- comment.reactions = reactions
304
- comment.mentions = mentions
305
- comment.save()
306
-
307
- EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentUpdated, body={
308
- "message": "A comment was edited by {username}",
309
- "username": g.user.username
310
- }, user_id=g.user.id, run_id=run_id, release_id=comment.release_id, test_id=test_id)
311
-
312
- return self.get_run_comments(run_id=run_id)
313
-
314
- def get_run_events(self, run_id: UUID):
315
- response = {}
316
- all_events = ArgusEvent.filter(run_id=run_id).all()
317
- all_events = sorted(all_events, key=lambda ev: ev.created_at)
318
- response["run_id"] = run_id
319
- response["raw_events"] = [dict(event.items()) for event in all_events]
320
- response["events"] = {
321
- str(event.id): EVENT_PROCESSORS.get(event.kind)(json.loads(event.body))
322
- for event in all_events
323
- }
324
- return response
325
-
326
-
327
- def submit_github_issue(self, issue_url: str, test_id: UUID, run_id: UUID):
328
- user_tokens = UserOauthToken.filter(user_id=g.user.id).all()
329
- token = None
330
- for tok in user_tokens:
331
- if tok.kind == "github":
332
- token = tok.token
333
- break
334
- if not token:
335
- raise Exception("Github token not found")
336
-
337
- match = re.match(
338
- r"http(s)?://(www\.)?github\.com/(?P<owner>[\w\d]+)/"
339
- r"(?P<repo>[\w\d\-_]+)/(?P<type>issues|pull)/(?P<issue_number>\d+)(/)?",
340
- issue_url,
341
- )
342
- if not match:
343
- raise Exception("URL doesn't match Github schema")
344
-
345
- test: ArgusTest = ArgusTest.get(id=test_id)
346
- plugin = self.get_plugin(plugin_name=test.plugin_name)
347
-
348
- run = plugin.model.get(id=run_id)
349
- release = ArgusRelease.get(id=run["release_id"])
350
- test = ArgusTest.get(build_system_id=run["build_id"])
351
- group = ArgusGroup.get(id=test.group_id)
352
-
353
- new_issue = ArgusGithubIssue()
354
- new_issue.user_id = g.user.id
355
- new_issue.run_id = run_id
356
- new_issue.group_id = group.id
357
- new_issue.release_id = release.id
358
- new_issue.test_id = test.id
359
- new_issue.type = match.group("type")
360
- new_issue.owner = match.group("owner")
361
- new_issue.repo = match.group("repo")
362
- new_issue.issue_number = int(match.group("issue_number"))
363
-
364
- issue_request = requests.get(
365
- f"https://api.github.com/repos/{new_issue.owner}/{new_issue.repo}/issues/{new_issue.issue_number}",
366
- headers={
367
- **self.github_headers,
368
- "Authorization": f"token {token}",
369
- }
370
- )
371
- if issue_request.status_code != 200:
372
- raise Exception(f"Error getting issue state: Response: HTTP {issue_request.status_code}", issue_request.json())
373
-
374
- issue_state: dict[str, Any] = issue_request.json()
375
-
376
- new_issue.title = issue_state.get("title")
377
- new_issue.url = issue_state.get("html_url")
378
- new_issue.last_status = issue_state.get("state")
379
- new_issue.save()
380
-
381
- EventService.create_run_event(
382
- kind=ArgusEventTypes.TestRunIssueAdded,
383
- body={
384
- "message": "An issue titled \"{title}\" was added by {username}",
385
- "username": g.user.username,
386
- "url": issue_url,
387
- "title": issue_state.get("title"),
388
- "state": issue_state.get("state"),
389
- },
390
- user_id=g.user.id,
391
- run_id=new_issue.run_id,
392
- release_id=new_issue.release_id,
393
- group_id=new_issue.group_id,
394
- test_id=new_issue.test_id
395
- )
396
-
397
- response = {
398
- **dict(list(new_issue.items())),
399
- "title": issue_state.get("title"),
400
- "state": issue_state.get("state"),
401
- }
402
-
403
- return response
404
-
405
- def _get_github_issues_for_view(self, view_id: UUID | str) -> list[ArgusGithubIssue]:
406
- view: ArgusUserView = ArgusUserView.get(id=view_id)
407
- issues = []
408
- for batch in chunk(view.tests):
409
- issues.extend(ArgusGithubIssue.filter(test_id__in=batch).allow_filtering().all())
410
-
411
- return issues
412
-
413
- def get_github_issues(self, filter_key: str, filter_id: UUID, aggregate_by_issue: bool = False) -> dict:
414
- if filter_key not in ["release_id", "group_id", "test_id", "run_id", "user_id", "view_id"]:
415
- raise Exception(
416
- "filter_key can only be one of: \"release_id\", \"group_id\", \"test_id\", \"run_id\", \"user_id\", \"view_id\""
417
- )
418
- if filter_key == "view_id":
419
- all_issues = self._get_github_issues_for_view(filter_id)
420
- else:
421
- all_issues = ArgusGithubIssue.filter(**{filter_key: filter_id}).all()
422
- if aggregate_by_issue:
423
- runs_by_issue = {}
424
- response = []
425
- for issue in all_issues:
426
- runs = runs_by_issue.get(issue, [])
427
- runs.append({"test_id": issue.test_id, "run_id": issue.run_id})
428
- runs_by_issue[issue] = runs
429
-
430
- for issue, runs in runs_by_issue.items():
431
- issue_dict = dict(issue.items())
432
- issue_dict["runs"] = runs
433
- response.append(issue_dict)
434
-
435
- else:
436
- response = [dict(issue.items()) for issue in all_issues]
437
- return response
438
-
439
- def resolve_run_build_id_and_number_multiple(self, runs: list[tuple[UUID, UUID]]) -> dict[UUID, dict[str, Any]]:
440
- test_ids = [r[0] for r in runs]
441
- all_tests: list = []
442
- for id_slice in chunk(test_ids):
443
- all_tests.extend(ArgusTest.filter(id__in=id_slice).all())
444
-
445
- tests: dict[str, ArgusTest] = {str(t.id): t for t in all_tests}
446
- runs_by_plugin = reduce(lambda acc, val: acc[tests[val[0]].plugin_name].append(val[1]) or acc, runs, defaultdict(list))
447
- all_runs = {}
448
- for plugin, run_ids in runs_by_plugin.items():
449
- model = AVAILABLE_PLUGINS.get(plugin).model
450
- model_runs = []
451
- for run_id in run_ids:
452
- model_runs.append(model.filter(id=run_id).only(["build_id", "start_time", "build_job_url", "id", "test_id"]).get())
453
- all_runs.update({ str(run["id"]): {**run, "build_number": get_build_number(run["build_job_url"])} for run in model_runs })
454
-
455
- return all_runs
456
-
457
-
458
- def delete_github_issue(self, issue_id: UUID) -> dict:
459
- issue: ArgusGithubIssue = ArgusGithubIssue.get(id=issue_id)
460
-
461
- EventService.create_run_event(
462
- kind=ArgusEventTypes.TestRunIssueRemoved,
463
- body={
464
- "message": "An issue titled \"{title}\" was removed by {username}",
465
- "username": g.user.username,
466
- "url": issue.url,
467
- "title": issue.title,
468
- "state": issue.last_status,
469
- },
470
- user_id=g.user.id,
471
- run_id=issue.run_id,
472
- release_id=issue.release_id,
473
- group_id=issue.group_id,
474
- test_id=issue.test_id
475
- )
476
- issue.delete()
477
-
478
- return {
479
- "deleted": issue_id
480
- }
481
-
482
- def terminate_stuck_runs(self):
483
- sct = AVAILABLE_PLUGINS.get("scylla-cluster-tests").model
484
- now = datetime.utcnow()
485
- stuck_period = now - timedelta(minutes=45)
486
- stuck_runs_running = sct.filter(heartbeat__lt=int(
487
- stuck_period.timestamp()), status=TestStatus.RUNNING.value).allow_filtering().all()
488
- stuck_runs_created = sct.filter(heartbeat__lt=int(
489
- stuck_period.timestamp()), status=TestStatus.CREATED.value).allow_filtering().all()
490
-
491
- all_stuck_runs = [*stuck_runs_running, *stuck_runs_created]
492
- LOGGER.info("Found %s stuck runs", len(all_stuck_runs))
493
-
494
- for run in all_stuck_runs:
495
- LOGGER.info("Will set %s as ABORTED", run.id)
496
- old_status = run.status
497
- run.status = TestStatus.ABORTED.value
498
- run.save()
499
-
500
- EventService.create_run_event(
501
- kind=ArgusEventTypes.TestRunStatusChanged,
502
- body={
503
- "message": "Run was automatically terminated due to not responding for more than 45 minutes "
504
- "(Status changed from {old_status} to {new_status}) by {username}",
505
- "old_status": old_status,
506
- "new_status": run.status,
507
- "username": g.user.username
508
- },
509
- user_id=g.user.id,
510
- run_id=run.id,
511
- release_id=run.release_id,
512
- group_id=run.group_id,
513
- test_id=run.test_id
514
- )
515
-
516
- return len(all_stuck_runs)
517
-
518
- def ignore_jobs(self, test_id: UUID, reason: str):
519
- test: ArgusTest = ArgusTest.get(id=test_id)
520
- plugin = self.get_plugin(plugin_name=test.plugin_name)
521
-
522
- if not reason:
523
- raise TestRunServiceException("Reason for ignore cannot be empty")
524
-
525
- cluster = ScyllaCluster.get()
526
- batch = BatchStatement(consistency_level=ConsistencyLevel.QUORUM)
527
- event_batch = BatchQuery()
528
- jobs_affected = 0
529
- for job in plugin.model.get_jobs_meta_by_test_id(test.id):
530
- if job["status"] != TestStatus.PASSED and job["investigation_status"] == TestInvestigationStatus.NOT_INVESTIGATED:
531
- batch.add(
532
- plugin.model.prepare_investigation_status_update_query(
533
- build_id=job["build_id"],
534
- start_time=job["start_time"],
535
- new_status=TestInvestigationStatus.IGNORED
536
- )
537
- )
538
-
539
- ArgusEvent.batch(event_batch).create(
540
- release_id = job["release_id"],
541
- group_id = job["group_id"],
542
- test_id = test_id,
543
- user_id = g.user.id,
544
- run_id = job["id"],
545
- body = json.dumps({
546
- "message": "Run was marked as ignored by {username} due to the following reason: {reason}",
547
- "username": g.user.username,
548
- "reason": reason,
549
- }, ensure_ascii=True, separators=(',', ':')),
550
- kind = ArgusEventTypes.TestRunBatchInvestigationStatusChange.value,
551
- created_at = datetime.utcnow(),
552
- )
553
-
554
- jobs_affected += 1
555
-
556
- cluster.session.execute(batch)
557
- event_batch.execute()
558
-
559
- return jobs_affected