feat(grafana): add support for multiple Grafana instances - #7527
Conversation
Changed Packages
|
|
Thanks for the contribution! |
Add the ability to configure multiple Grafana instances under a `grafana.hosts` config key. Each host can have its own domain, proxy path, and alerting mode. Entities are associated with a specific instance via the `grafana/source-id` annotation. Key changes: - New `grafana.hosts[]` config schema with per-host settings - New `grafana/source-id` entity annotation - Merged GrafanaApiClient to handle both unified and legacy alerting per-host instead of globally - AlertsCard resolves unifiedAlerting per-host, not from global config - Shared readHosts() config utility with proxy path validation - Full backward compatibility with single-instance `grafana.domain` Signed-off-by: André Ahlert <andre@aex.partners>
388de1b to
4eb589f
Compare
|
Hey @awanlin, this implements the multiple Grafana instances support from #1293. I based the approach on K-Phoen/backstage-plugin-grafana#76 but adapted it to the current codebase, with some improvements: per-host I've been actively contributing to community-plugins lately - created the n8n workspace (#7522), submitted a few fixes for sentry (#7523, #7525, #7526), and now this. I've been focusing on workspaces that could use some extra attention, and I'd be happy to help maintain this one going forward. |
|
Hi @awanlin, this PR implements multi-instance Grafana support as requested in #1293. I've kept full backward compatibility with the existing single-instance config while adding the new I've also opened #7528 adding unit test coverage for the API clients (GrafanaApiClient and UnifiedAlertingGrafanaApiClient), which previously had none. Would you be able to review or suggest another reviewer? Happy to address any feedback. |
|
Hi @andreahlert, while we appreciate contributions, let's hold off on conversations regarding ownership changes. I'd also suggest reading our Contributing guide as that covers the review process: https://github.com/backstage/community-plugins/blob/main/CONTRIBUTING.md#review-process. I'd also suggest you read over the AI Policy in the upstream Backstage repo as that very much applies here as well: https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#ai-use-policy-and-guidelines. This repo is volunteer run, reviews will be slow as they are based on Plugin Owner availability. Traditionally I only have time for them on Friday afternoons. I just happen to be doing some work and spotted your PRs yesterday and wanted to help set you on the right footing as they had some good and not good aspects to them. Today I'm just following up as I was ping multiple times and wanted to know why. 👍 |
Hey @awanlin, noted on all points. About the AI policy and Contributing Guide: I went through it, I take code quality seriously and like to keep things well documented, which might come across as over-structured sometimes. I wasn't trying to push for ownership either, just wanted to show I'm around and interested in helping out. And yeah, the multiple pings that's an annoying habit of mine, sorry about that 😅. I promise I'll behave, I don't want to be that guy who makes you regret checking GitHub on a Saturday. I set aside one day a week for Backstage stuff but got a bit carried away and spent some extra hours because I was enjoying the work. I contribute to other repos too so I get how volunteer time works, so no rush on the review. Thanks for taking the time to point me in the right direction. |
2dee0ac to
750a182
Compare
b86856b to
4f88f27
Compare
f33b999 to
261ff2b
Compare
- Remove custom GrafanaConfigApi interface, use ConfigApi from @backstage/core-plugin-api - When both grafana.domain and grafana.hosts are set, ignore domain and log warning - Remove @public from GrafanaApiClient class - Rename grafana/source-id to grafana/host-id annotation - Remove per-host grafanaDashboardSearchLimit/grafanaDashboardMaxPages, keep global only - Mark global grafanaDashboardSearchLimit/grafanaDashboardMaxPages as @deprecated Signed-off-by: André Ahlert <andre@aex.partners>
Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com> Signed-off-by: André Ahlert <andre@aex.partners>
Signed-off-by: André Ahlert <andre@aex.partners>
Signed-off-by: André Ahlert <andre@aex.partners>
Add eslint-disable comments for intentional use of deprecated config keys (grafana.domain, grafana.proxyPath, grafana.unifiedAlerting, grafanaDashboardSearchLimit, grafanaDashboardMaxPages) so CI list-deprecations check passes while keeping backward compatibility. - Fix indent of eslint-enable in config.ts - Use dashboardSearchLimit/dashboardMaxPages in GrafanaApiClientOptions to match plugin.ts and apis.ts (fix type checking). Signed-off-by: André Ahlert <andre@aex.partners>
Add defaultHostId to GrafanaApiClientOptions in report.api.md so check api reports and generate API reference CI step passes. Signed-off-by: André Ahlert <andre@aex.partners>
- Regenerated report.api.md and report-alpha.api.md using backstage-repo-tools\n- Includes the new defaultHostId property in GrafanaApiClientOptions\n- Fixes CI validation failure on 'check api reports and generate API reference' Signed-off-by: André Ahlert <andre@aex.partners>
Regenerate report.api.md and report-alpha.api.md to match the current API Extractor output used in CI, fixing the build:api-reports:only --ci failure on PR 7527. Signed-off-by: André Ahlert <andre@aex.partners>
Update EntityGrafanaAlertsCard and EntityGrafanaDashboardsCard signatures in report.api.md to match API Extractor output from CI (Node 22/24), resolving the remaining api report mismatch. Signed-off-by: André Ahlert <andre@aex.partners>
Update report-alpha.api.md from API Extractor output generated under Node 22/24 to match CI expectations and fix build:api-reports:only --ci failures. Signed-off-by: André Ahlert <andre@aex.partners>
Resolve add/add conflict in api.test.ts by combining both test suites and format with prettier. Signed-off-by: André Ahlert <andre@aex.partners>
bb6fdd5 to
e4e7f4a
Compare
|
Hi @andreahlert, a conflict crept in, maybe a rebase should help sort that out 👍 |
|
Hi @vinzscam, can you give this another look, please? |
Sync with latest upstream changes: - Update createApiRef to new .with() syntax - Regenerate report.api.md with updated ApiRef type signature - Pick up grafana dependency bumps (fast-xml-parser, flatted) Signed-off-by: André Ahlert <andre@aex.partners>
130a828 to
0bf723f
Compare
There was a problem hiding this comment.
Pull request overview
Adds multi-instance Grafana support to the @backstage-community/plugin-grafana workspace plugin by introducing host-aware configuration + entity-level host selection, while keeping the legacy single-instance config working.
Changes:
- Introduces
grafana.hosts[]+grafana.defaultHostconfig parsing/validation and wires it into both classic and@alphaAPI factories. - Extends the Grafana API/client to resolve a host per request and handle unified vs legacy alerting on a per-host basis.
- Updates entity cards/components + docs/tests to pass and document a
grafana/host-idannotation.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| workspaces/grafana/plugins/grafana/src/types.ts | Adds GrafanaHost type used across config/client. |
| workspaces/grafana/plugins/grafana/src/plugin.ts | Switches plugin API factory to readHosts() + new multi-host client options. |
| workspaces/grafana/plugins/grafana/src/index.ts | Exports new public GrafanaApiClientOptions type. |
| workspaces/grafana/plugins/grafana/src/constants.ts | Adds grafana/host-id annotation constant + helper. |
| workspaces/grafana/plugins/grafana/src/constants.test.ts | Adds tests for hostIdFromEntity. |
| workspaces/grafana/plugins/grafana/src/config.ts | New config reader/validator for legacy + multi-host configs. |
| workspaces/grafana/plugins/grafana/src/config.test.ts | New tests for config parsing/validation behavior. |
| workspaces/grafana/plugins/grafana/src/components/DashboardsCard/DashboardsCard.tsx | Passes entity host id through to dashboard API calls. |
| workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.tsx | Uses per-host isUnifiedAlerting() and passes host id to alerts API calls. |
| workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.test.tsx | Adds integration tests ensuring host-aware unified/legacy selector behavior. |
| workspaces/grafana/plugins/grafana/src/api.ts | Merges clients into a single multi-host client + adds isUnifiedAlerting. |
| workspaces/grafana/plugins/grafana/src/api.test.ts | Expands tests for host resolution + unified/legacy alerting behavior. |
| workspaces/grafana/plugins/grafana/src/alpha/entityCards.test.tsx | Updates Grafana API mock to include isUnifiedAlerting. |
| workspaces/grafana/plugins/grafana/src/alpha/apis.ts | Updates alpha API factory to use readHosts() and new client options. |
| workspaces/grafana/plugins/grafana/src/fixtures/entity.ts | Adds a fixture entity including grafana/host-id. |
| workspaces/grafana/plugins/grafana/report.api.md | Updates API report for new exports/types. |
| workspaces/grafana/plugins/grafana/docs/setup.md | Documents multi-instance config + grafana/host-id. |
| workspaces/grafana/plugins/grafana/docs/dashboards-on-component-page.md | Documents selecting host for dashboards via annotation. |
| workspaces/grafana/plugins/grafana/docs/alerts-on-component-page.md | Documents selecting host for alerts via annotation. |
| workspaces/grafana/plugins/grafana/config.d.ts | Extends config schema for hosts[] + defaultHost, deprecates legacy keys. |
| workspaces/grafana/.changeset/grafana-multiple-instances.md | Declares a minor release with multi-instance support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
workspaces/grafana/plugins/grafana/src/components/DashboardsCard/DashboardsCard.tsx:112
useAsyncis called without a dependency array even though the dashboards query depends onentityannotations (selector + host-id) andopts.additionalDashboards. If the entity/annotations change without a full remount, this hook won’t refetch and can show stale data or query the wrong Grafana instance. Consider supplying an explicit deps array (e.g. based ondashboardSelectorFromEntity(entity),hostIdFromEntity(entity), andopts.additionalDashboards).
const grafanaApi = useApi(grafanaApiRef);
const { value, loading, error } = useAsync(async () => {
const dashboards = await grafanaApi.listDashboards(
dashboardSelectorFromEntity(entity),
hostIdFromEntity(entity),
);
if (opts?.additionalDashboards) {
dashboards.push(...opts.additionalDashboards(entity));
}
return dashboards;
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use typed ConfigApi getters for grafana.hosts parsing - Validate unique host ids and non-empty id/domain fields - Fix eslint-disable scope for deprecation rule - Guard isUnifiedAlerting against synchronous throws in AlertsCard - Remove unused identityApiRef dependency in alpha API - Use exported constant in tests instead of literal string Signed-off-by: André Ahlert <andre@aex.partners>
GrafanaApiClient is a public export and can be constructed directly, bypassing readHosts validation. Throw explicitly on duplicate host ids in initClients so silent overwrites surface as misconfiguration. Signed-off-by: André Ahlert <andre@aex.partners>
- Validate `defaultHostId` exists in constructor and stop silently falling back to first host when an explicit defaultHostId is unknown. - Iterate every selector in legacy alerting instead of dropping all but the first when an array is passed. - Skip alert fetching and add an explicit dependency array in `AlertsCard` so the request reruns when the entity annotations change and short-circuits when host resolution fails. - Share `DEFAULT_PROXY_PATH` between `api.ts` and `config.ts` to keep the proxy collision validation aligned with the runtime default. Signed-off-by: André Ahlert <andre@aex.partners>
dd530e0 to
5d843cb
Compare
|
thank you @andreahlert 🙏 |
Summary
Closes #1293
Adds the ability to configure multiple Grafana instances, allowing organizations with separate Grafana deployments (e.g., production and staging) to use them all within Backstage.
grafana.hosts[]configuration with per-hostdomain,proxyPath,unifiedAlerting, and search settingsgrafana/host-identity annotation to associate entities with specific Grafana instancesgrafana.domainconfigurationChanges
Core:
config.d.ts- Addedhosts[]config schemasrc/types.ts- AddedGrafanaHostinterfacesrc/constants.ts- AddedGRAFANA_ANNOTATION_HOST_IDandhostIdFromEntity()src/api.ts- Merged two API client classes into one multi-host client withisUnifiedAlerting()per-host resolutionsrc/config.ts- SharedreadHosts()utility with validationsrc/plugin.ts/src/alpha/apis.ts- Updated factories using shared configComponents:
AlertsCard.tsx- Uses per-hostisUnifiedAlerting()instead of global configDashboardsCard.tsx- PasseshostIdto API callsTests (+24 new tests):
api.test.ts- Host resolution,isUnifiedAlertingper-host, fallback, error casesconfig.test.ts- Config reading, validation, proxy path collisionconstants.test.ts-hostIdFromEntitywith/without annotationAlertsCard.test.tsx- Integration tests for per-host unified alerting selector flowDocs:
setup.md- Multi-instance configuration guidealerts-on-component-page.md/dashboards-on-component-page.md-grafana/host-idusageConfiguration example
Test plan
grafana/host-idfalls back to default host