Skip to content

Commit dbf1a54

Browse files
authored
refactor: unversion tree and hardware listing flows (#1923)
* Promote the v2 tree and hardware listing implementations to unversioned routes and APIs * Remove legacy v1 endpoints, pages, feature flags. * Remove orphaned checkout-cache code. Closes #1917 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
1 parent b74dd8a commit dbf1a54

73 files changed

Lines changed: 1067 additions & 3966 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/kernelCI/settings.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,6 @@ def get_json_env_var(name, default):
167167
"CRONTAB_COMMAND_SUFFIX", ">> /proc/1/fd/1 2>&1"
168168
)
169169
CRONJOBS = [
170-
# not using a monitoring_id in the first task since it should
171-
# be removed once the denormalization is set in stone
172-
("0 * * * *", "kernelCI_app.tasks.update_checkout_cache"),
173170
(
174171
"59 * * * *",
175172
"django.core.management.call_command",

backend/kernelCI_app/management/commands/seed_test_data.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99

1010
from kernelCI_app.constants.general import UNKNOWN_STRING
1111
from kernelCI_app.helpers.system import get_running_instance
12+
from kernelCI_app.management.commands.helpers.aggregation_helpers import simplify_status
1213
from kernelCI_app.management.commands.helpers.process_pending_helpers import (
1314
accumulate_rollup_entry,
1415
extract_path_group,
1516
)
1617
from kernelCI_app.models import (
1718
Builds,
1819
Checkouts,
20+
HardwareStatus,
1921
Incidents,
2022
Issues,
23+
LatestCheckout,
24+
SimplifiedStatusChoices,
2125
StatusChoices,
2226
Tests,
2327
TreeTestsRollup,
@@ -31,6 +35,13 @@
3135
)
3236
from kernelCI_app.tests.factories.mocks import Build, Issue, Test
3337

38+
STATUS_FIELD_BY_SIMPLIFIED_STATUS = {
39+
SimplifiedStatusChoices.PASS: "pass",
40+
SimplifiedStatusChoices.FAIL: "failed",
41+
SimplifiedStatusChoices.INCONCLUSIVE: "inc",
42+
None: "inc",
43+
}
44+
3445

3546
class Command(BaseCommand):
3647
help = "Seed test database with realistic data for integration tests"
@@ -74,6 +85,8 @@ def handle(self, *args, **options):
7485
issues = self.create_issues(count=options["issues"])
7586
incidents = self.create_incidents(issues=issues, builds=builds, tests=tests)
7687
rollup_rows = self.create_tests_rollup(tests=tests, incidents=incidents)
88+
latest_checkouts = self.create_latest_checkouts(checkouts=checkouts)
89+
hardware_rows = self.create_hardware_status(tests=tests)
7790

7891
self.stdout.write(
7992
self.style.SUCCESS(
@@ -84,6 +97,8 @@ def handle(self, *args, **options):
8497
f"- {len(issues)} issues\n"
8598
f"- {len(incidents)} incidents\n"
8699
f"- {len(rollup_rows)} tree_tests_rollup rows\n"
100+
f"- {len(latest_checkouts)} latest_checkout rows\n"
101+
f"- {len(hardware_rows)} hardware_status rows\n"
87102
)
88103
)
89104

@@ -112,6 +127,8 @@ def _validate_clear_operation(self, *, skip_confirmation: bool) -> None:
112127

113128
def clear_data(self) -> None:
114129
"""Clear existing test data."""
130+
HardwareStatus.objects.all().delete()
131+
LatestCheckout.objects.all().delete()
115132
TreeTestsRollup.objects.all().delete()
116133
Incidents.objects.all().delete()
117134
Issues.objects.all().delete()
@@ -335,3 +352,90 @@ def create_tests_rollup(
335352

336353
created = TreeTestsRollup.objects.bulk_create(rollup_objects)
337354
return created
355+
356+
def create_latest_checkouts(
357+
self, *, checkouts: list[Checkouts]
358+
) -> list[LatestCheckout]:
359+
"""Create latest_checkout rows used by listing queries."""
360+
self.stdout.write("Creating latest_checkout rows...")
361+
362+
latest_by_tree: dict[tuple, Checkouts] = {}
363+
for checkout in checkouts:
364+
key = (
365+
checkout.origin,
366+
checkout.tree_name,
367+
checkout.git_repository_url,
368+
checkout.git_repository_branch,
369+
)
370+
current = latest_by_tree.get(key)
371+
if current is None or checkout.start_time > current.start_time:
372+
latest_by_tree[key] = checkout
373+
374+
latest_checkouts = [
375+
LatestCheckout(
376+
checkout_id=checkout.id,
377+
start_time=checkout.start_time,
378+
origin=checkout.origin,
379+
tree_name=checkout.tree_name,
380+
git_repository_url=checkout.git_repository_url,
381+
git_repository_branch=checkout.git_repository_branch,
382+
)
383+
for checkout in latest_by_tree.values()
384+
]
385+
386+
return LatestCheckout.objects.bulk_create(latest_checkouts)
387+
388+
def create_hardware_status(self, *, tests: list[Tests]) -> list[HardwareStatus]:
389+
"""Aggregate seeded tests into hardware_status rows."""
390+
self.stdout.write("Creating hardware_status aggregations...")
391+
392+
hardware_data: dict[tuple, dict] = {}
393+
counted_builds_by_key: dict[tuple, set[str]] = {}
394+
395+
for test in tests:
396+
platform = (
397+
test.environment_misc.get("platform") if test.environment_misc else None
398+
)
399+
if platform is None:
400+
continue
401+
402+
checkout = test.build.checkout
403+
key = (test.origin, platform, checkout.id)
404+
if key not in hardware_data:
405+
hardware_data[key] = {
406+
"checkout_id": checkout.id,
407+
"test_origin": test.origin,
408+
"platform": platform,
409+
"compatibles": test.environment_compatible,
410+
"start_time": checkout.start_time,
411+
"build_pass": 0,
412+
"build_failed": 0,
413+
"build_inc": 0,
414+
"boot_pass": 0,
415+
"boot_failed": 0,
416+
"boot_inc": 0,
417+
"test_pass": 0,
418+
"test_failed": 0,
419+
"test_inc": 0,
420+
}
421+
counted_builds_by_key[key] = set()
422+
423+
record = hardware_data[key]
424+
status_type = STATUS_FIELD_BY_SIMPLIFIED_STATUS[
425+
simplify_status(test.status)
426+
]
427+
428+
test_prefix = "boot" if (test.path or "").startswith("boot") else "test"
429+
record[f"{test_prefix}_{status_type}"] += 1
430+
431+
if test.build_id not in counted_builds_by_key[
432+
key
433+
] and not test.build_id.startswith("maestro:dummy_"):
434+
counted_builds_by_key[key].add(test.build_id)
435+
build_status_type = STATUS_FIELD_BY_SIMPLIFIED_STATUS[
436+
simplify_status(test.build.status)
437+
]
438+
record[f"build_{build_status_type}"] += 1
439+
440+
hardware_rows = [HardwareStatus(**data) for data in hardware_data.values()]
441+
return HardwareStatus.objects.bulk_create(hardware_rows)

backend/kernelCI_app/queries/hardware.py

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -109,98 +109,6 @@ def _get_hardware_listing_count_clauses() -> str:
109109
return build_count_clause + boot_count_clause + test_count_clause
110110

111111

112-
def get_hardware_listing_data(
113-
*,
114-
start_date: datetime,
115-
end_date: datetime,
116-
origin: str,
117-
commits_list: Optional[list[str]] = None,
118-
) -> list[dict]:
119-
"""
120-
Retrieves the listing of platform, compatibles, and
121-
the status counts of builds, boots and tests
122-
for the latest checkout of every tree.
123-
The selected checkouts and tests are limited to the start_date/end_date interval.
124-
When commits_list is set, tree heads are not used: checkouts qualify if
125-
git_commit_hash is in the token list or git_commit_tags overlaps it (comma-
126-
separated request values can be full SHAs or tag strings stored on checkouts).
127-
Still scoped by the test start_time and origin filters below.
128-
"""
129-
130-
count_clauses = _get_hardware_listing_count_clauses()
131-
132-
params = {
133-
"start_date": start_date,
134-
"end_date": end_date,
135-
"origin": origin,
136-
}
137-
138-
if commits_list:
139-
params["commits_list"] = commits_list
140-
checkout_ids_select = """
141-
SELECT C.id
142-
FROM checkouts C
143-
WHERE C.git_commit_hash = ANY(%(commits_list)s)
144-
OR (
145-
C.git_commit_tags IS NOT NULL
146-
AND C.git_commit_tags && %(commits_list)s::text[]
147-
)
148-
"""
149-
else:
150-
checkout_ids_select = _get_hardware_tree_heads_clause(id_only=True)
151-
152-
selected_checkouts_cte = f"""
153-
selected_checkouts AS (
154-
{checkout_ids_select}
155-
),
156-
"""
157-
158-
# The grouping by platform and compatibles is possible because a platform
159-
# can dictate the array of compatibles, meaning that if the array of compatibles
160-
# is different, then the platform should/must be different as well.
161-
#
162-
# It is possible that a platform appears for tests from origin A which are related
163-
# to checkouts from origin B. It's for those cases that the origin filter is applied
164-
# to the tests, not checkouts. There are no platforms being tested by multiple origins yet.
165-
query = f"""
166-
WITH
167-
{selected_checkouts_cte}
168-
relevant_tests AS (
169-
SELECT
170-
"tests"."environment_compatible" AS hardware,
171-
"tests"."environment_misc" ->> 'platform' AS platform,
172-
"tests"."status",
173-
"tests"."path",
174-
"tests"."id",
175-
b.id AS build_id,
176-
b.status AS build_status
177-
FROM
178-
tests
179-
INNER JOIN builds b ON tests.build_id = b.id
180-
JOIN selected_checkouts AC ON b.checkout_id = AC.id
181-
WHERE
182-
"tests"."environment_misc" ->> 'platform' IS NOT NULL
183-
AND "tests"."origin" = %(origin)s
184-
AND "tests"."start_time" >= %(start_date)s
185-
AND "tests"."start_time" <= %(end_date)s
186-
)
187-
-- From the raw rows, selects platform, hardware, and performs the status grouping.
188-
SELECT
189-
platform,
190-
hardware,
191-
{count_clauses}
192-
FROM
193-
relevant_tests
194-
GROUP BY
195-
platform,
196-
hardware
197-
"""
198-
199-
with connection.cursor() as cursor:
200-
cursor.execute(query, params)
201-
return cursor.fetchall()
202-
203-
204112
def get_hardware_selectors(origin: str) -> list[dict]:
205113
cache_key = "hardwareSelectors"
206114
cache_params = {"origin": origin}

backend/kernelCI_app/queries/notifications.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def kcidb_last_test_without_issue(issue, incident):
368368
return kcidb_execute_query(query, params)
369369

370370

371-
# Similar to get_tree_listing_data, but at the same time it has to be different.
371+
# Similar to the tree listing summary query, but with notification-specific filters.
372372
# Only the "with", "join" and "where" clauses change
373373
def get_checkout_summary_data(
374374
*,

0 commit comments

Comments
 (0)