Add per-tenant alert generator URL template for customizable alert source links#7302
Add per-tenant alert generator URL template for customizable alert source links#7302friedrichg merged 16 commits intocortexproject:masterfrom
Conversation
Add support for tenants to configure alert GeneratorURL to use Grafana Explore format instead of the default Prometheus /graph format. This is controlled by three new per-tenant settings: ruler_alert_generator_url_format, ruler_grafana_datasource_uid, and ruler_grafana_org_id. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
yeya24
left a comment
There was a problem hiding this comment.
@rajagopalanand Can you help take a look?
friedrichg
left a comment
There was a problem hiding this comment.
I gave a review, but I think we do not care what parameters are send to the url. We don't care about grafana org IDs
I think we should make this general enough to be used by any metrics explorer.
Maybe consider renaming the function as explore and not have any grafana
I am thinking of something that would support for example:
explore
what do you think?
…nerator URLs Replace the 3 Grafana-specific per-tenant config fields (ruler_alert_generator_url_format, ruler_grafana_datasource_uid, ruler_grafana_org_id) with a single generic field: ruler_alert_generator_url_template. This field accepts a Go text/template string with .ExternalURL and .Expression variables, plus built-in functions like urlquery. Users can construct any URL format (Grafana, Perses, etc.) without Cortex needing to understand specific UI formats. The ruler_external_url per-tenant override and SendAlerts signature (func(expr string) string) are kept unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Add per-tenant Alertmanager datasources (tenant-a, tenant-b) to Grafana provisioning so alerts are visible in Grafana's alerting UI. Add runtime-config.yaml with per-tenant overrides: - tenant-a: Grafana Explore URL template with full pane JSON - tenant-b: Perses explore URL template with PrometheusTimeSeriesQuery Update Perses from v0.49 to v0.53.1 and enable the explorer feature (frontend.explorer.enable: true). Rename project from "default" to "cortex" to match template URLs. Add Step 7 to the getting-started guide with instructions for: - Configuring per-tenant alert generator URL templates - Loading alertmanager configs and demo alert rules - Viewing alerts in Grafana at /alerting/groups?groupBy=alertname - Verifying generator URLs via the API Also configure ruler.alertmanager_url and ruler.external_url, and set an explicit UID on the Grafana Cortex datasource for use in templates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Add support for tenants to configure alert GeneratorURL to use Grafana Explore format instead of the default Prometheus /graph format. This is controlled by three new per-tenant settings: ruler_alert_generator_url_format, ruler_grafana_datasource_uid, and ruler_grafana_org_id. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
…nerator URLs Replace the 3 Grafana-specific per-tenant config fields (ruler_alert_generator_url_format, ruler_grafana_datasource_uid, ruler_grafana_org_id) with a single generic field: ruler_alert_generator_url_template. This field accepts a Go text/template string with .ExternalURL and .Expression variables, plus built-in functions like urlquery. Users can construct any URL format (Grafana, Perses, etc.) without Cortex needing to understand specific UI formats. The ruler_external_url per-tenant override and SendAlerts signature (func(expr string) string) are kept unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Add per-tenant Alertmanager datasources (tenant-a, tenant-b) to Grafana provisioning so alerts are visible in Grafana's alerting UI. Add runtime-config.yaml with per-tenant overrides: - tenant-a: Grafana Explore URL template with full pane JSON - tenant-b: Perses explore URL template with PrometheusTimeSeriesQuery Update Perses from v0.49 to v0.53.1 and enable the explorer feature (frontend.explorer.enable: true). Rename project from "default" to "cortex" to match template URLs. Add Step 7 to the getting-started guide with instructions for: - Configuring per-tenant alert generator URL templates - Loading alertmanager configs and demo alert rules - Viewing alerts in Grafana at /alerting/groups?groupBy=alertname - Verifying generator URLs via the API Also configure ruler.alertmanager_url and ruler.external_url, and set an explicit UID on the Grafana Cortex datasource for use in templates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Resolve conflict in schemas/cortex-config-schema.json: keep both the upstream results_cache_ttl entry and our ruler_alert_generator_url_template. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Resolve conflict in schemas/cortex-config-schema.json: keep both results_cache_ttl and ruler_alert_generator_url_template entries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
Signed-off-by: Charlie Le <charlie_le@apple.com>
- Set ExternalURL on rules.ManagerOptions to the per-tenant override so
{{ $externalURL }} in alert annotation/label templates reflects the
tenant's ruler_external_url, not just the global config.
- Fix Grafana datasource: add explicit uid: tenant-a so the template
reference "datasource":"tenant-a" resolves correctly.
- Fix runtime-config.yaml template to reference "datasource":"tenant-a"
instead of "datasource":"cortex".
- Add text/template security comment explaining the intentional choice
over html/template.
- Add CHANGELOG entry for the ruler_alert_generator_url_template feature.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Charlie Le <charlie_le@apple.com>
| if tmplStr == "" { | ||
| return externalURLStr + strutil.TableLinkForExpression(expr) | ||
| } | ||
| result, err := executeGeneratorURLTemplate(tmplStr, externalURLStr, expr) |
There was a problem hiding this comment.
This anonymous function is called in SendAlerts and do we need to evaluate the template every time? This is not a bug but wondering if there is a way to optimize it
There was a problem hiding this comment.
we could definitely cache it. I agree
There was a problem hiding this comment.
Added caching in the latest commit. Thanks!
rajagopalanand
left a comment
There was a problem hiding this comment.
Overall looks good. Had 2 questions
Validate that the output of executeGeneratorURLTemplate produces a safe URL by checking: - Scheme must be http or https (blocks javascript: and data: URIs) - Host must be present - Fragment must not contain HTML characters < or > (blocks script injection) Add test cases covering javascript URI, data URI, fragment injection, missing host, and valid fragment scenarios. Signed-off-by: Friedrich Gonzalez <charlie_le@apple.com> Signed-off-by: Friedrich Gonzalez <1517449+friedrichg@users.noreply.github.com>
The NotifyFunc closure was capturing externalURLStr once at manager creation time, so runtime config changes to ruler_external_url would not take effect until the ruler was restarted. Move the resolution into a helper that re-reads from overrides on each call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
The generator URL template is parsed from the runtime config string on every alert send. Cache the parsed template and only re-parse when the template string changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Charlie Le <charlie_le@apple.com>
|
I am going to merge. Please keep commenting if something still needs to be addressed |
Summary
ruler_alert_generator_url_templatefield that accepts a Gotext/templatestringruler_external_urloverride so tenants can have different external URLs without changing the global ruler configWhen
ruler_alert_generator_url_templateis set, the ruler evaluates the template with.ExternalURLand.Expressionvariables to produce the alert'sGeneratorURL. Built-in Go template functions likeurlqueryare available for URL encoding. If the template is empty, the default Prometheus/graphformat is used.New per-tenant config options
ruler_external_url""(uses global)ruler_alert_generator_url_template""(Prometheus format)Example runtime config
Getting-started docker-compose example
The PR includes a working docker-compose example in
docs/getting-started/with:Test plan
executeGeneratorURLTemplatewith various expressions,urlquery, and invalid templatesuserExternalURLtracking per-tenant URL changesSendAlertswith custom generator URL function (default + template formats)Limits.Validate()catches invalid templates at config load time🤖 Generated with Claude Code