Skip to content

Commit

Permalink
v1.13.0
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyorlando authored Nov 20, 2024
2 parents ff63d03 + fda05a6 commit fde4596
Show file tree
Hide file tree
Showing 79 changed files with 1,875 additions and 346 deletions.
1 change: 1 addition & 0 deletions .github/workflows/linting-and-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ jobs:
grafana_version:
- 10.3.0
- 11.2.0
- latest
fail-fast: false
with:
grafana_version: ${{ matrix.grafana_version }}
Expand Down
24 changes: 22 additions & 2 deletions Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ def plugin_json():
return plugin_file
return 'NOT_A_PLUGIN'

def extra_grafana_ini():
return {
'feature_toggles': {
'accessControlOnCall': 'false'
}
}

def extra_env():
return {
"GF_APP_URL": grafana_url,
"GF_SERVER_ROOT_URL": grafana_url,
"GF_FEATURE_TOGGLES_ENABLE": "externalServiceAccounts",
"ONCALL_API_URL": "http://oncall-dev-engine:8080"
"ONCALL_API_URL": "http://oncall-dev-engine:8080",

# Enables managed service accounts for plugin authentication in Grafana >= 11.3
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#managed_service_accounts_enabled
"GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED": "true",
}

def extra_deps():
Expand Down Expand Up @@ -132,7 +143,16 @@ def load_grafana():
"GF_APP_URL": grafana_url, # older versions of grafana need this
"GF_SERVER_ROOT_URL": grafana_url,
"GF_FEATURE_TOGGLES_ENABLE": "externalServiceAccounts",
"ONCALL_API_URL": "http://oncall-dev-engine:8080"
"ONCALL_API_URL": "http://oncall-dev-engine:8080",

# Enables managed service accounts for plugin authentication in Grafana >= 11.3
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#managed_service_accounts_enabled
"GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED": "true",
},
extra_grafana_ini={
"feature_toggles": {
"accessControlOnCall": "false"
}
},
)
# --- GRAFANA END ----
Expand Down
5 changes: 4 additions & 1 deletion dev/helm-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ engine:
replicaCount: 1
celery:
replicaCount: 1
worker_beat_enabled: false
worker_beat_enabled: true

externalGrafana:
url: http://grafana:3000

grafana:
enabled: false
grafana.ini:
feature_toggles:
accessControlOnCall: false
server:
domain: localhost:3000
root_url: "%(protocol)s://%(domain)s"
Expand All @@ -71,6 +73,7 @@ grafana:
value: oncallpassword
env:
GF_FEATURE_TOGGLES_ENABLE: externalServiceAccounts
GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED: true
GF_SECURITY_ADMIN_PASSWORD: oncall
GF_SECURITY_ADMIN_USER: oncall
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
Expand Down
1 change: 1 addition & 0 deletions docker-compose-developer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ services:
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
GF_FEATURE_TOGGLES_ENABLE: externalServiceAccounts
ONCALL_API_URL: http://host.docker.internal:8080
GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED: true
env_file:
- ./dev/.env.${DB}.dev
ports:
Expand Down
10 changes: 10 additions & 0 deletions docker-compose-mysql-rabbitmq.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ services:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin}
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
GF_INSTALL_PLUGINS: grafana-oncall-app
GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED: true
deploy:
resources:
limits:
Expand All @@ -156,7 +157,16 @@ services:
condition: service_healthy
profiles:
- with_grafana
configs:
- source: grafana.ini
target: /etc/grafana/grafana.ini

volumes:
dbdata:
rabbitmqdata:

configs:
grafana.ini:
content: |
[feature_toggles]
accessControlOnCall = false
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ services:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin}
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
GF_INSTALL_PLUGINS: grafana-oncall-app
GF_AUTH_MANAGED_SERVICE_ACCOUNTS_ENABLED: true
volumes:
- grafana_data:/var/lib/grafana
deploy:
Expand All @@ -103,9 +104,18 @@ services:
cpus: "0.5"
profiles:
- with_grafana
configs:
- source: grafana.ini
target: /etc/grafana/grafana.ini

volumes:
grafana_data:
prometheus_data:
oncall_data:
redis_data:

configs:
grafana.ini:
content: |
[feature_toggles]
accessControlOnCall = false
3 changes: 1 addition & 2 deletions docs/sources/configure/jinja2-templating/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/oncall/configure/integrations/references/webhook/
---


## Configure templates
# Configure templates

Grafana OnCall integrates with your monitoring systems using webhooks with JSON payloads.
By default, these webhooks deliver raw JSON payloads.
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/alerts/migrations/0001_squashed_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Migration(migrations.Migration):
name='AlertGroupPostmortem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('public_primary_key', models.CharField(default=apps.alerts.models.resolution_note.generate_public_primary_key_for_alert_group_postmortem, max_length=20, unique=True, validators=[django.core.validators.MinLengthValidator(13)])),
('public_primary_key', models.CharField(max_length=20, unique=True, validators=[django.core.validators.MinLengthValidator(13)])),
('created_at', models.DateTimeField(auto_now_add=True)),
('last_modified', models.DateTimeField(auto_now=True)),
('text', models.TextField(default=None, max_length=3000, null=True)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.15 on 2024-11-12 13:13

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('user_management', '0027_serviceaccount'),
('alerts', '0064_migrate_resolutionnoteslackmessage_slack_channel_id'),
]

operations = [
migrations.AddField(
model_name='alertreceivechannel',
name='service_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='alert_receive_channels', to='user_management.serviceaccount'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 4.2.16 on 2024-11-06 21:11

from django.db import migrations
import django_migration_linter as linter


class Migration(migrations.Migration):

dependencies = [
('alerts', '0065_alertreceivechannel_service_account'),
]

operations = [
linter.IgnoreMigration(),
migrations.RemoveField(
model_name='channelfilter',
name='_slack_channel_id',
),
migrations.RemoveField(
model_name='resolutionnoteslackmessage',
name='_slack_channel_id',
),
migrations.DeleteModel(
name='AlertGroupPostmortem',
),
]
32 changes: 16 additions & 16 deletions engine/apps/alerts/models/alert_receive_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject):
author = models.ForeignKey(
"user_management.User", on_delete=models.SET_NULL, related_name="alert_receive_channels", blank=True, null=True
)
service_account = models.ForeignKey(
"user_management.ServiceAccount",
on_delete=models.SET_NULL,
related_name="alert_receive_channels",
blank=True,
null=True,
)
team = models.ForeignKey(
"user_management.Team",
on_delete=models.SET_NULL,
Expand Down Expand Up @@ -518,29 +525,21 @@ def short_name(self):
)

@property
def short_name_with_maintenance_status(self):
if self.maintenance_mode is not None:
return (
self.short_name + f" *[ on "
f"{AlertReceiveChannel.MAINTENANCE_MODE_CHOICES[self.maintenance_mode][1]}"
f" :construction: ]*"
)
else:
return self.short_name

@property
def created_name(self):
def created_name(self) -> str:
return f"{self.get_integration_display()} {self.smile_code}"

@property
def web_link(self) -> str:
return UIURLBuilder(self.organization).integration_detail(self.public_primary_key)

@property
def is_maintenace_integration(self) -> bool:
return self.integration == AlertReceiveChannel.INTEGRATION_MAINTENANCE

@property
def integration_url(self) -> str | None:
if self.integration in [
AlertReceiveChannel.INTEGRATION_MANUAL,
AlertReceiveChannel.INTEGRATION_SLACK_CHANNEL,
AlertReceiveChannel.INTEGRATION_INBOUND_EMAIL,
AlertReceiveChannel.INTEGRATION_MAINTENANCE,
]:
Expand Down Expand Up @@ -764,15 +763,16 @@ def listen_for_alertreceivechannel_model_save(
from apps.heartbeat.models import IntegrationHeartBeat

if created:
write_resource_insight_log(instance=instance, author=instance.author, event=EntityEvent.CREATED)
author = instance.author or instance.service_account
write_resource_insight_log(instance=instance, author=author, event=EntityEvent.CREATED)
default_filter = ChannelFilter(alert_receive_channel=instance, filtering_term=None, is_default=True)
default_filter.save()
write_resource_insight_log(instance=default_filter, author=instance.author, event=EntityEvent.CREATED)
write_resource_insight_log(instance=default_filter, author=author, event=EntityEvent.CREATED)

TEN_MINUTES = 600 # this is timeout for cloud heartbeats
if instance.is_available_for_integration_heartbeat:
heartbeat = IntegrationHeartBeat.objects.create(alert_receive_channel=instance, timeout_seconds=TEN_MINUTES)
write_resource_insight_log(instance=heartbeat, author=instance.author, event=EntityEvent.CREATED)
write_resource_insight_log(instance=heartbeat, author=author, event=EntityEvent.CREATED)

metrics_add_integrations_to_cache([instance], instance.organization)

Expand Down
4 changes: 0 additions & 4 deletions engine/apps/alerts/models/channel_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,13 @@ class ChannelFilter(OrderedModel):

notify_in_slack = models.BooleanField(null=True, default=True)
notify_in_telegram = models.BooleanField(null=True, default=False)

# TODO: remove _slack_channel_id in future release
_slack_channel_id = models.CharField(max_length=100, null=True, default=None)
slack_channel = models.ForeignKey(
"slack.SlackChannel",
null=True,
default=None,
on_delete=models.SET_NULL,
related_name="+",
)

telegram_channel = models.ForeignKey(
"telegram.TelegramToOrganizationConnector",
on_delete=models.SET_NULL,
Expand Down
50 changes: 8 additions & 42 deletions engine/apps/alerts/models/resolution_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,7 @@
if typing.TYPE_CHECKING:
from apps.alerts.models import AlertGroup
from apps.slack.models import SlackChannel


def generate_public_primary_key_for_alert_group_postmortem():
prefix = "P"
new_public_primary_key = generate_public_primary_key(prefix)

failure_counter = 0
while AlertGroupPostmortem.objects.filter(public_primary_key=new_public_primary_key).exists():
new_public_primary_key = increase_public_primary_key_length(
failure_counter=failure_counter, prefix=prefix, model_name="AlertGroupPostmortem"
)
failure_counter += 1

return new_public_primary_key
from apps.user_management.models import User


def generate_public_primary_key_for_resolution_note():
Expand Down Expand Up @@ -75,17 +62,13 @@ class ResolutionNoteSlackMessage(models.Model):
related_name="added_resolution_note_slack_messages",
)
text = models.TextField(max_length=3000, default=None, null=True)

# TODO: remove _slack_channel_id in future release
_slack_channel_id = models.CharField(max_length=100, null=True, default=None)
slack_channel = models.ForeignKey(
"slack.SlackChannel",
null=True,
default=None,
on_delete=models.SET_NULL,
related_name="+",
)

ts = models.CharField(max_length=100, null=True, default=None)
thread_ts = models.CharField(max_length=100, null=True, default=None)
permalink = models.CharField(max_length=250, null=True, default=None)
Expand Down Expand Up @@ -130,6 +113,7 @@ def filter(self, *args, **kwargs):

class ResolutionNote(models.Model):
alert_group: "AlertGroup"
author: typing.Optional["User"]
resolution_note_slack_message: typing.Optional[ResolutionNoteSlackMessage]

objects = ResolutionNoteQueryset.as_manager()
Expand Down Expand Up @@ -213,29 +197,11 @@ def render_log_line_json(self):

return result

def author_verbal(self, mention):
"""
Postmortems to resolution notes included migrating AlertGroupPostmortem to ResolutionNotes.
But AlertGroupPostmortem has no author field. So this method was introduces as workaround.
def author_verbal(self, mention: bool) -> str:
"""
if self.author is not None:
return self.author.get_username_with_slack_verbal(mention)
else:
return ""
Postmortems to resolution notes included migrating `AlertGroupPostmortem` to `ResolutionNote`s.
But `AlertGroupPostmortem` has no author field. So this method was introduced as a workaround.

class AlertGroupPostmortem(models.Model):
public_primary_key = models.CharField(
max_length=20,
validators=[MinLengthValidator(settings.PUBLIC_PRIMARY_KEY_MIN_LENGTH + 1)],
unique=True,
default=generate_public_primary_key_for_alert_group_postmortem,
)
alert_group = models.ForeignKey(
"alerts.AlertGroup",
on_delete=models.CASCADE,
related_name="postmortem_text",
)
created_at = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
text = models.TextField(max_length=3000, default=None, null=True)
(see git history for more details on what `AlertGroupPostmortem` was)
"""
return "" if self.author is None else self.author.get_username_with_slack_verbal(mention)
Loading

0 comments on commit fde4596

Please sign in to comment.