argus-alm 0.12.10__py3-none-any.whl → 0.13.1__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 (94) 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 +22 -2
  5. argus/client/generic/client.py +22 -0
  6. argus/client/generic_result.py +3 -3
  7. argus/client/sct/client.py +5 -4
  8. argus/client/sirenada/client.py +1 -1
  9. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/METADATA +2 -4
  10. argus_alm-0.13.1.dist-info/RECORD +20 -0
  11. argus/backend/.gitkeep +0 -0
  12. argus/backend/cli.py +0 -41
  13. argus/backend/controller/__init__.py +0 -0
  14. argus/backend/controller/admin.py +0 -20
  15. argus/backend/controller/admin_api.py +0 -354
  16. argus/backend/controller/api.py +0 -529
  17. argus/backend/controller/auth.py +0 -67
  18. argus/backend/controller/client_api.py +0 -108
  19. argus/backend/controller/main.py +0 -274
  20. argus/backend/controller/notification_api.py +0 -72
  21. argus/backend/controller/notifications.py +0 -13
  22. argus/backend/controller/team.py +0 -126
  23. argus/backend/controller/team_ui.py +0 -18
  24. argus/backend/controller/testrun_api.py +0 -482
  25. argus/backend/controller/view_api.py +0 -162
  26. argus/backend/db.py +0 -100
  27. argus/backend/error_handlers.py +0 -21
  28. argus/backend/events/event_processors.py +0 -34
  29. argus/backend/models/__init__.py +0 -0
  30. argus/backend/models/result.py +0 -138
  31. argus/backend/models/web.py +0 -389
  32. argus/backend/plugins/__init__.py +0 -0
  33. argus/backend/plugins/core.py +0 -225
  34. argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  35. argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  36. argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
  37. argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
  38. argus/backend/plugins/driver_matrix_tests/service.py +0 -60
  39. argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
  40. argus/backend/plugins/generic/model.py +0 -79
  41. argus/backend/plugins/generic/plugin.py +0 -16
  42. argus/backend/plugins/generic/types.py +0 -13
  43. argus/backend/plugins/loader.py +0 -40
  44. argus/backend/plugins/sct/controller.py +0 -185
  45. argus/backend/plugins/sct/plugin.py +0 -38
  46. argus/backend/plugins/sct/resource_setup.py +0 -178
  47. argus/backend/plugins/sct/service.py +0 -491
  48. argus/backend/plugins/sct/testrun.py +0 -272
  49. argus/backend/plugins/sct/udt.py +0 -101
  50. argus/backend/plugins/sirenada/model.py +0 -113
  51. argus/backend/plugins/sirenada/plugin.py +0 -17
  52. argus/backend/service/admin.py +0 -27
  53. argus/backend/service/argus_service.py +0 -688
  54. argus/backend/service/build_system_monitor.py +0 -188
  55. argus/backend/service/client_service.py +0 -122
  56. argus/backend/service/event_service.py +0 -18
  57. argus/backend/service/jenkins_service.py +0 -240
  58. argus/backend/service/notification_manager.py +0 -150
  59. argus/backend/service/release_manager.py +0 -230
  60. argus/backend/service/results_service.py +0 -317
  61. argus/backend/service/stats.py +0 -540
  62. argus/backend/service/team_manager_service.py +0 -83
  63. argus/backend/service/testrun.py +0 -559
  64. argus/backend/service/user.py +0 -307
  65. argus/backend/service/views.py +0 -258
  66. argus/backend/template_filters.py +0 -27
  67. argus/backend/tests/__init__.py +0 -0
  68. argus/backend/tests/argus_web.test.yaml +0 -39
  69. argus/backend/tests/conftest.py +0 -44
  70. argus/backend/tests/results_service/__init__.py +0 -0
  71. argus/backend/tests/results_service/test_best_results.py +0 -70
  72. argus/backend/util/common.py +0 -65
  73. argus/backend/util/config.py +0 -38
  74. argus/backend/util/encoders.py +0 -41
  75. argus/backend/util/logsetup.py +0 -81
  76. argus/backend/util/module_loaders.py +0 -30
  77. argus/backend/util/send_email.py +0 -91
  78. argus/client/generic_result_old.py +0 -143
  79. argus/db/.gitkeep +0 -0
  80. argus/db/argus_json.py +0 -14
  81. argus/db/cloud_types.py +0 -125
  82. argus/db/config.py +0 -135
  83. argus/db/db_types.py +0 -139
  84. argus/db/interface.py +0 -370
  85. argus/db/testrun.py +0 -740
  86. argus/db/utils.py +0 -15
  87. argus_alm-0.12.10.dist-info/RECORD +0 -96
  88. /argus/{backend → common}/__init__.py +0 -0
  89. /argus/{backend/util → common}/enums.py +0 -0
  90. /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
  91. /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
  92. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/LICENSE +0 -0
  93. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/WHEEL +0 -0
  94. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.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