Skip to content

Commit 1a3c76a

Browse files
committed
feat: add metric button to enable disable metrics
1 parent be7353d commit 1a3c76a

File tree

7 files changed

+148
-25
lines changed

7 files changed

+148
-25
lines changed

controller/sentry/admin.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from django_json_widget.widgets import JSONEditorWidget
1515
from django_object_actions import DjangoObjectActions, takes_instance_or_queryset
1616

17-
from controller.sentry.forms import BumpForm
17+
from controller.sentry.choices import MetricType
18+
from controller.sentry.forms import BumpForm, MetricForm
1819
from controller.sentry.models import App
1920
from controller.sentry.utils import invalidate_cache
2021

@@ -63,32 +64,36 @@ class AppAdmin(
6364
[
6465
"Sentry",
6566
{
66-
"classes": ("collapse", "open"),
67+
"classes": ("collapse",),
6768
"fields": ("sentry_project_slug",),
6869
},
6970
],
7071
[
71-
"WSGI",
72+
"Metric - WSGI",
7273
{
73-
"classes": ("collapse", "open"),
74-
"fields": ("wsgi_ignore_path", "wsgi_collect_metrics", "wsgi_metrics"),
74+
"classes": ("collapse",),
75+
"fields": (
76+
"wsgi_collect_metrics",
77+
"wsgi_ignore_path",
78+
"wsgi_metrics",
79+
),
7580
},
7681
],
7782
[
78-
"Celery",
83+
"Metric - Celery",
7984
{
80-
"classes": ("collapse", "open"),
85+
"classes": ("collapse",),
8186
"fields": (
82-
"celery_ignore_task",
8387
"celery_collect_metrics",
88+
"celery_ignore_task",
8489
"celery_metrics",
8590
),
8691
},
8792
],
8893
]
8994
actions = ["bump_sample_rate"]
9095
changelist_actions = ["panic", "unpanic"]
91-
change_actions = ["bump_sample_rate"]
96+
change_actions = ["bump_sample_rate", "enable_disable_metrics"]
9297

9398
def get_changelist_actions(self, request):
9499
allowed_actions = []
@@ -104,6 +109,7 @@ def get_change_actions(self, request, object_id, form_url):
104109
allowed_actions.append(action)
105110
return allowed_actions
106111

112+
# ----- Bump Sample Rate
107113
@takes_instance_or_queryset
108114
@add_form_to_action(BumpForm)
109115
@confirm_action()
@@ -114,6 +120,8 @@ def bump_sample_rate(self, request, queryset, form: BumpForm = None): # pylint:
114120
active_sample_rate=form.cleaned_data["new_sample_rate"],
115121
active_window_end=new_date,
116122
)
123+
for app in queryset:
124+
invalidate_cache(f"/sentry/apps/{app.reference}/")
117125

118126
bump_sample_rate.allowed_permissions = ("bump_sample_rate",)
119127

@@ -125,6 +133,29 @@ def has_bump_sample_rate_permission(self, request):
125133
panic = cache.get(settings.PANIC_KEY)
126134
return not panic and request.user.has_perm("%s.%s" % (opts.app_label, codename))
127135

136+
# ----- Update Metrics
137+
@takes_instance_or_queryset
138+
@add_form_to_action(MetricForm)
139+
@admin.action(description="Enable/Disable Metrics Collection")
140+
def enable_disable_metrics(self, request, queryset, form: MetricForm = None): # pylint: disable=unused-argument
141+
metrics = form.cleaned_data["metrics"]
142+
app: App
143+
for app in queryset:
144+
for metric in MetricType:
145+
app.set_metric(metric, metric.value in metrics)
146+
invalidate_cache(f"/sentry/apps/{app.reference}/")
147+
app.save()
148+
149+
enable_disable_metrics.allowed_permissions = ("enable_disable_metrics",)
150+
151+
def has_enable_disable_metrics_permission(self, request):
152+
"""Does the user have the enable_disable_metrics permission?"""
153+
opts = self.opts
154+
codename = get_permission_codename("enable_disable_metrics", opts)
155+
156+
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
157+
158+
# ----- Panic / Unpanic
128159
@takes_instance_or_queryset
129160
@confirm_action(display_queryset=False)
130161
@admin.action(description="Panic")
@@ -157,6 +188,7 @@ def has_unpanic_permission(self, request):
157188
codename = get_permission_codename("panic", opts)
158189
return panic and request.user.has_perm("%s.%s" % (opts.app_label, codename))
159190

191+
# Save model
160192
def save_model(self, request, obj, form, change) -> None:
161193
invalidate_cache(f"/sentry/apps/{obj.reference}/")
162194
return super().save_model(request, obj, form, change)

controller/sentry/forms.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
from django.conf import settings
22
from django.core.exceptions import ValidationError
3-
from django.forms import DurationField, FloatField, Form
3+
from django.forms import DurationField, FloatField, Form, MultipleChoiceField
4+
from django.forms.widgets import CheckboxSelectMultiple
45
from durationwidget.widgets import TimeDurationWidget
56

7+
from controller.sentry.choices import MetricType
8+
69

710
class BumpForm(Form):
8-
new_sample_rate = FloatField(help_text="Sample rate between 0 and 1")
11+
new_sample_rate = FloatField(help_text="Sample rate between 0 and 1", initial=0.5)
912
duration = DurationField(
10-
widget=TimeDurationWidget(show_days=False, show_seconds=False),
11-
help_text="Duration",
13+
widget=TimeDurationWidget(show_days=False, show_seconds=False), help_text="Duration", initial="0:30:00"
1214
)
1315

1416
def clean_new_sample_rate(self):
@@ -22,3 +24,12 @@ def clean_duration(self):
2224
if data.total_seconds() < 0 or data.total_seconds() > settings.MAX_BUMP_TIME_SEC:
2325
raise ValidationError(f"duration must be between 0 and {settings.MAX_BUMP_TIME_SEC}")
2426
return data
27+
28+
29+
class MetricForm(Form):
30+
metrics = MultipleChoiceField(
31+
choices=MetricType.choices,
32+
widget=CheckboxSelectMultiple,
33+
required=False,
34+
help_text="Disable or Enable metric gathering",
35+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generated by Django 4.1.6 on 2023-02-03 14:36
2+
3+
from django.core.management.sql import emit_post_migrate_signal
4+
from django.db import migrations
5+
6+
7+
def apply_migration(apps, schema_editor):
8+
emit_post_migrate_signal(2, False, "default")
9+
Group = apps.get_model("auth", "Group")
10+
Permission = apps.get_model("auth", "Permission")
11+
owner = Group.objects.get(name="Owner")
12+
admin = Group.objects.get(name="Admin")
13+
developer = Group.objects.get(name="Developer")
14+
15+
metric_permission = Permission.objects.get(codename="enable_disable_metrics_app")
16+
17+
owner.permissions.add(metric_permission)
18+
admin.permissions.add(metric_permission)
19+
developer.permissions.add(metric_permission)
20+
21+
22+
class Migration(migrations.Migration):
23+
24+
dependencies = [
25+
("sentry", "0007_app_sentry_project_slug"),
26+
]
27+
28+
operations = [
29+
migrations.AlterModelOptions(
30+
name="app",
31+
options={
32+
"permissions": [
33+
("bump_sample_rate_app", "Can bump sample rate"),
34+
("panic_app", "Panic! Set all sample rate to 0"),
35+
("enable_disable_metrics_app", "Can Enable/Disable metrics"),
36+
]
37+
},
38+
),
39+
migrations.RunPython(apply_migration, migrations.RunPython.noop),
40+
]

controller/sentry/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ def get_metric(self, metric_type: MetricType):
6161
prefix = metric_type.value.lower()
6262
return getattr(self, f"{prefix}_collect_metrics"), getattr(self, f"{prefix}_metrics")
6363

64+
def set_metric(self, metric_type: MetricType, metric_state: bool):
65+
prefix = metric_type.value.lower()
66+
setattr(self, f"{prefix}_collect_metrics", metric_state)
67+
6468
def get_sentry_id(self):
6569
res = self.reference.split("_")
6670
if len(res) != 3:
@@ -71,4 +75,5 @@ class Meta:
7175
permissions = [
7276
("bump_sample_rate_app", "Can bump sample rate"),
7377
("panic_app", "Panic! Set all sample rate to 0"),
78+
("enable_disable_metrics_app", "Can Enable/Disable metrics"),
7479
]

controller/sentry/tests/test_admin.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from undecorated import undecorated
88

99
from controller.sentry.admin import AppAdmin
10-
from controller.sentry.forms import BumpForm
10+
from controller.sentry.forms import BumpForm, MetricForm
1111
from controller.sentry.models import App
1212

1313

@@ -146,6 +146,41 @@ def test_app_admin_bump(request, admin_with_user):
146146
assert app.active_window_end is not None
147147

148148

149+
@pytest.mark.django_db
150+
@pytest.mark.parametrize("user_group", ["Developer"])
151+
@pytest.mark.admin_site(model_class=App)
152+
def test_app_admin_metrics(request, admin_with_user):
153+
app = App(wsgi_collect_metrics=False, celery_collect_metrics=False)
154+
app.save()
155+
form = MetricForm({"metrics": ["WSGI", "CELERY"]})
156+
157+
assert form.is_valid()
158+
site, request = admin_with_user
159+
enable_disable_metrics = undecorated(site.enable_disable_metrics)
160+
enable_disable_metrics(site, request, App.objects.filter(reference=app.reference), form=form)
161+
app.refresh_from_db()
162+
assert app.wsgi_collect_metrics
163+
assert app.celery_collect_metrics
164+
165+
form = MetricForm({"metrics": []})
166+
assert form.is_valid()
167+
site, request = admin_with_user
168+
enable_disable_metrics = undecorated(site.enable_disable_metrics)
169+
enable_disable_metrics(site, request, App.objects.filter(reference=app.reference), form=form)
170+
app.refresh_from_db()
171+
assert not app.wsgi_collect_metrics
172+
assert not app.celery_collect_metrics
173+
174+
form = MetricForm({"metrics": ["WSGI"]})
175+
assert form.is_valid()
176+
site, request = admin_with_user
177+
enable_disable_metrics = undecorated(site.enable_disable_metrics)
178+
enable_disable_metrics(site, request, App.objects.filter(reference=app.reference), form=form)
179+
app.refresh_from_db()
180+
assert app.wsgi_collect_metrics
181+
assert not app.celery_collect_metrics
182+
183+
149184
@patch("controller.sentry.admin.cache")
150185
@pytest.mark.django_db
151186
@pytest.mark.parametrize("user_group", ["Developer"])
@@ -197,13 +232,13 @@ def test_app_admin_get_changelist_actions(cache: Mock, admin_with_user, result,
197232
@pytest.mark.parametrize(
198233
"user_group,panic,result",
199234
[
200-
("Owner", False, ["bump_sample_rate"]),
201-
("Admin", False, ["bump_sample_rate"]),
202-
("Developer", False, ["bump_sample_rate"]),
235+
("Owner", False, ["bump_sample_rate", "enable_disable_metrics"]),
236+
("Admin", False, ["bump_sample_rate", "enable_disable_metrics"]),
237+
("Developer", False, ["bump_sample_rate", "enable_disable_metrics"]),
203238
("Viewer", False, []),
204-
("Owner", True, []),
205-
("Admin", True, []),
206-
("Developer", True, []),
239+
("Owner", True, ["enable_disable_metrics"]),
240+
("Admin", True, ["enable_disable_metrics"]),
241+
("Developer", True, ["enable_disable_metrics"]),
207242
("Viewer", True, []),
208243
],
209244
)

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ packages = [{ include = "controller" }]
99
[tool.poetry.dependencies]
1010
python = "^3.9"
1111
Django = "^4.1.3"
12-
django-admin-action-tools = "^1.1"
12+
django-admin-action-tools = "1.1.5"
1313
djangorestframework = "^3.14.0"
1414
gunicorn = "^20.1.0"
1515
psycopg2-binary = "^2.9.5"

0 commit comments

Comments
 (0)