From 983aeb1b89309c07912bf0d5b4838ad131218ef4 Mon Sep 17 00:00:00 2001 From: alexandru-io <47977085+alexandru-io@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:43:33 +0300 Subject: [PATCH] Add TaskclusterMetadata to the alert summary API endpoint (#7740) Bug 1838836 - Add TaskclusterMetadata to the alert summary API endpoint --- tests/conftest.py | 70 +++++++++++++++++++ .../webapp/api/test_performance_alerts_api.py | 20 +++++- .../api/test_performance_alertsummary_api.py | 36 +++++++++- .../webapp/api/performance_serializers.py | 42 ++++++++++- 4 files changed, 164 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index afcfb7b170e..e678896289b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -351,6 +351,11 @@ def test_job_2(eleven_job_blobs, create_jobs): return create_jobs(eleven_job_blobs[0:2])[1] +@pytest.fixture +def test_job_3(eleven_job_blobs, create_jobs): + return create_jobs(eleven_job_blobs[0:3])[2] + + @pytest.fixture def mock_log_parser(monkeypatch): from celery import shared_task @@ -669,6 +674,32 @@ def create_perf_signature( ) +@pytest.fixture +def test_taskcluster_metadata(test_job_2) -> th_models.TaskclusterMetadata: + return create_taskcluster_metadata(test_job_2) + + +@pytest.fixture +def test_taskcluster_metadata_2(test_job_3) -> th_models.TaskclusterMetadata: + return create_taskcluster_metadata_2(test_job_3) + + +def create_taskcluster_metadata(test_job_2) -> th_models.TaskclusterMetadata: + return th_models.TaskclusterMetadata.objects.create( + job=test_job_2, + task_id='V3SVuxO8TFy37En_6HcXLp', + retry_id='0', + ) + + +def create_taskcluster_metadata_2(test_job_3) -> th_models.TaskclusterMetadata: + return th_models.TaskclusterMetadata.objects.create( + job=test_job_3, + task_id='V3SVuxO8TFy37En_6HcXLq', + retry_id='0', + ) + + @pytest.fixture def test_perf_signature_2(test_perf_signature): return perf_models.PerformanceSignature.objects.create( @@ -922,11 +953,50 @@ def test_perf_alert_summary_with_bug( ) +@pytest.fixture +def test_perf_datum(test_repository, test_perf_signature, test_job_2): + push = th_models.Push.objects.get(id=1) + perf_models.PerformanceDatum.objects.create( + repository=test_repository, + job=test_job_2, + push_id=1, + signature=test_perf_signature, + value=1, + push_timestamp=push.time, + ) + + +@pytest.fixture +def test_perf_datum_2(test_repository, test_perf_signature, test_job_3): + push = th_models.Push.objects.get(id=2) + perf_models.PerformanceDatum.objects.create( + repository=test_repository, + job=test_job_3, + push_id=2, + signature=test_perf_signature, + value=1, + push_timestamp=push.time, + ) + + @pytest.fixture def test_perf_alert(test_perf_signature, test_perf_alert_summary) -> perf_models.PerformanceAlert: return create_perf_alert(summary=test_perf_alert_summary, series_signature=test_perf_signature) +@pytest.fixture +def test_perf_alert_with_tcmetadata( + test_perf_signature, test_perf_alert_summary +) -> perf_models.PerformanceAlert: + perf_alert = create_perf_alert( + summary=test_perf_alert_summary, series_signature=test_perf_signature + ) + perf_alert.taskcluster_metadata = test_taskcluster_metadata_2 + perf_alert.prev_taskcluster_metadata = test_taskcluster_metadata + perf_alert.save() + return perf_alert + + def create_perf_alert(**alert_properties) -> perf_models.PerformanceAlert: defaults = dict( amount_abs=50.0, diff --git a/tests/webapp/api/test_performance_alerts_api.py b/tests/webapp/api/test_performance_alerts_api.py index fe976a9715c..71658b148c2 100644 --- a/tests/webapp/api/test_performance_alerts_api.py +++ b/tests/webapp/api/test_performance_alerts_api.py @@ -8,7 +8,15 @@ from treeherder.perf.models import PerformanceAlert, PerformanceAlertSummary, PerformanceFramework -def test_alerts_get(client, test_repository, test_perf_alert): +def test_alerts_get( + client, + test_repository, + test_perf_alert_with_tcmetadata, + test_perf_datum, + test_perf_datum_2, + test_taskcluster_metadata, + test_taskcluster_metadata_2, +): resp = client.get(reverse('performance-alerts-list')) assert resp.status_code == 200 @@ -27,6 +35,8 @@ def test_alerts_get(client, test_repository, test_perf_alert): 'prev_value', 'related_summary_id', 'series_signature', + 'taskcluster_metadata', + 'prev_taskcluster_metadata', 'summary_id', 'status', 't_value', @@ -36,6 +46,14 @@ def test_alerts_get(client, test_repository, test_perf_alert): 'noise_profile', } assert resp.json()['results'][0]['related_summary_id'] is None + assert set(resp.json()['results'][0]['taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } + assert set(resp.json()['results'][0]['prev_taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } def test_alerts_put( diff --git a/tests/webapp/api/test_performance_alertsummary_api.py b/tests/webapp/api/test_performance_alertsummary_api.py index 69374065845..688822d8334 100644 --- a/tests/webapp/api/test_performance_alertsummary_api.py +++ b/tests/webapp/api/test_performance_alertsummary_api.py @@ -53,7 +53,15 @@ def test_perf_alert_onhold(test_perf_signature, test_perf_alert_summary_onhold) ) -def test_alert_summaries_get(client, test_perf_alert_summary, test_perf_alert): +def test_alert_summaries_get( + client, + test_perf_alert_summary, + test_perf_alert_with_tcmetadata, + test_perf_datum, + test_perf_datum_2, + test_taskcluster_metadata, + test_taskcluster_metadata_2, +): # verify that we get the performance summary + alert on GET resp = client.get(reverse('performance-alert-summaries-list')) assert resp.status_code == 200 @@ -89,6 +97,8 @@ def test_alert_summaries_get(client, test_perf_alert_summary, test_perf_alert): 'id', 'status', 'series_signature', + 'taskcluster_metadata', + 'prev_taskcluster_metadata', 'is_regression', 'starred', 'manually_created', @@ -105,12 +115,24 @@ def test_alert_summaries_get(client, test_perf_alert_summary, test_perf_alert): 'noise_profile', } assert resp.json()['results'][0]['related_alerts'] == [] + assert set(resp.json()['results'][0]['alerts'][0]['taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } + assert set(resp.json()['results'][0]['alerts'][0]['prev_taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } def test_alert_summaries_get_onhold( client, test_perf_alert_summary, - test_perf_alert, + test_perf_alert_with_tcmetadata, + test_perf_datum, + test_perf_datum_2, + test_taskcluster_metadata, + test_taskcluster_metadata_2, test_perf_alert_summary_onhold, test_perf_alert_onhold, test_repository_onhold, @@ -150,6 +172,8 @@ def test_alert_summaries_get_onhold( 'id', 'status', 'series_signature', + 'taskcluster_metadata', + 'prev_taskcluster_metadata', 'is_regression', 'starred', 'manually_created', @@ -166,6 +190,14 @@ def test_alert_summaries_get_onhold( 'noise_profile', } assert resp.json()['results'][0]['related_alerts'] == [] + assert set(resp.json()['results'][0]['alerts'][0]['taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } + assert set(resp.json()['results'][0]['alerts'][0]['prev_taskcluster_metadata'].keys()) == { + 'task_id', + 'retry_id', + } def test_alert_summaries_put( diff --git a/treeherder/webapp/api/performance_serializers.py b/treeherder/webapp/api/performance_serializers.py index 93da0a70c4a..ee9f2260802 100644 --- a/treeherder/webapp/api/performance_serializers.py +++ b/treeherder/webapp/api/performance_serializers.py @@ -5,7 +5,7 @@ from django.db import transaction from rest_framework import exceptions, serializers -from treeherder.model.models import Repository +from treeherder.model.models import Repository, TaskclusterMetadata from treeherder.perf.models import ( BackfillRecord, IssueTracker, @@ -118,6 +118,8 @@ class Meta: class PerformanceAlertSerializer(serializers.ModelSerializer): series_signature = PerformanceSignatureSerializer(read_only=True) + taskcluster_metadata = serializers.SerializerMethodField() + prev_taskcluster_metadata = serializers.SerializerMethodField() summary_id = serializers.SlugRelatedField( slug_field="id", source="summary", @@ -183,6 +185,42 @@ def update(self, instance, validated_data): return super().update(instance, validated_data) + def get_taskcluster_metadata(self, alert): + datum = PerformanceDatum.objects.filter( + signature=alert.series_signature, + repository=alert.series_signature.repository, + push=alert.summary.push, + ).first() + if datum: + try: + metadata = TaskclusterMetadata.objects.get(job=datum.job) + return { + 'task_id': metadata.task_id, + 'retry_id': metadata.retry_id, + } + except ObjectDoesNotExist: + return {} + else: + return {} + + def get_prev_taskcluster_metadata(self, alert): + datum = PerformanceDatum.objects.filter( + signature=alert.series_signature, + repository=alert.series_signature.repository, + push=alert.summary.prev_push, + ).first() + if datum: + try: + metadata = TaskclusterMetadata.objects.get(job=datum.job) + return { + 'task_id': metadata.task_id, + 'retry_id': metadata.retry_id, + } + except ObjectDoesNotExist: + return {} + else: + return {} + def get_classifier_email(self, performance_alert): return getattr(performance_alert.classifier, 'email', None) @@ -192,6 +230,8 @@ class Meta: 'id', 'status', 'series_signature', + 'taskcluster_metadata', + 'prev_taskcluster_metadata', 'is_regression', 'prev_value', 'new_value',