From f704b8aa798b4b56724c20b26379c6b4d9ce1a8e Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Wed, 14 Jan 2026 14:16:09 +0100 Subject: [PATCH] Cloud Monitoring: Add support for Google Cloud universe_domain (#115931) * feat(cloud-monitoring): add support for Google Cloud universe_domain (#110083) This change introduces support for Google Cloud's `universe_domain`, enabling connections to sovereign cloud environments with custom API endpoints. - Adds an optional "Universe Domain" field in the Google Cloud Monitoring data source configuration (frontend and backend). - Allows specifying a custom API domain (e.g., `s3nsapis.fr`) for use in sovereign environments. - Defaults to `googleapis.com` to ensure backward compatibility for existing configurations. Signed-off-by: Andreas Christou * Minor docs update * Doc updates * Update editor * Update docs/sources/datasources/google-cloud-monitoring/_index.md Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> * Lint * Review --------- Signed-off-by: Andreas Christou Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> --- .../google-cloud-monitoring/_index.md | 12 ++++--- pkg/tsdb/cloud-monitoring/cloudmonitoring.go | 9 ++++-- pkg/tsdb/cloud-monitoring/httpclient.go | 11 +++++-- .../cloud-monitoring/resource_handler_test.go | 2 +- .../components/ConfigEditor/ConfigEditor.tsx | 31 +++++++++++++++---- .../cloud-monitoring/types/types.ts | 1 + 6 files changed, 50 insertions(+), 16 deletions(-) diff --git a/docs/sources/datasources/google-cloud-monitoring/_index.md b/docs/sources/datasources/google-cloud-monitoring/_index.md index 27bc7b390cd..d5412c33ef7 100644 --- a/docs/sources/datasources/google-cloud-monitoring/_index.md +++ b/docs/sources/datasources/google-cloud-monitoring/_index.md @@ -103,10 +103,11 @@ To configure basic settings for the data source, complete the following steps: 1. Set the data source's basic configuration options: - | Name | Description | - | ----------- | ------------------------------------------------------------------------ | - | **Name** | Sets the name you use to refer to the data source in panels and queries. | - | **Default** | Sets whether the data source is pre-selected for new panels. | + | Name | Description | + | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | **Name** | Sets the name you use to refer to the data source in panels and queries. | + | **Default** | Sets whether the data source is pre-selected for new panels. | + | **Universe Domain** | The universe domain to connect to. For more information, refer to [Documentation on universe domains](https://docs.cloud.google.com/python/docs/reference/monitoring/latest/google.cloud.monitoring_v3.services.service_monitoring_service.ServiceMonitoringServiceAsyncClient#google_cloud_monitoring_v3_services_service_monitoring_service_ServiceMonitoringServiceAsyncClient_universe_domain). Defaults to `googleapis.com`. | ### Provision the data source @@ -129,6 +130,7 @@ datasources: clientEmail: stackdriver@myproject.iam.gserviceaccount.com authenticationType: jwt defaultProject: my-project-name + universeDomain: googleapis.com secureJsonData: privateKey: | -----BEGIN PRIVATE KEY----- @@ -152,6 +154,7 @@ datasources: clientEmail: stackdriver@myproject.iam.gserviceaccount.com authenticationType: jwt defaultProject: my-project-name + universeDomain: googleapis.com privateKeyPath: /etc/secrets/gce.pem ``` @@ -166,6 +169,7 @@ datasources: access: proxy jsonData: authenticationType: gce + universeDomain: googleapis.com ``` ## Import pre-configured dashboards diff --git a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go index c88feb30a1a..37401de02c4 100644 --- a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go +++ b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go @@ -92,7 +92,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque }, nil } - url := fmt.Sprintf("%v/v3/projects/%v/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject) + url := fmt.Sprintf("%s/v3/projects/%s/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject) request, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err @@ -139,6 +139,7 @@ type datasourceInfo struct { defaultProject string clientEmail string tokenUri string + universeDomain string services map[string]datasourceService privateKey string usingImpersonation bool @@ -150,6 +151,7 @@ type datasourceJSONData struct { DefaultProject string `json:"defaultProject"` ClientEmail string `json:"clientEmail"` TokenURI string `json:"tokenUri"` + UniverseDomain string `json:"universeDomain"` UsingImpersonation bool `json:"usingImpersonation"` ServiceAccountToImpersonate string `json:"serviceAccountToImpersonate"` } @@ -179,6 +181,7 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst defaultProject: jsonData.DefaultProject, clientEmail: jsonData.ClientEmail, tokenUri: jsonData.TokenURI, + universeDomain: jsonData.UniverseDomain, usingImpersonation: jsonData.UsingImpersonation, serviceAccountToImpersonate: jsonData.ServiceAccountToImpersonate, services: map[string]datasourceService{}, @@ -194,13 +197,13 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst return nil, err } - for name, info := range routes { + for name := range routes { client, err := newHTTPClient(dsInfo, opts, &httpClientProvider, name) if err != nil { return nil, err } dsInfo.services[name] = datasourceService{ - url: info.url, + url: buildURL(name, dsInfo.universeDomain), client: client, } } diff --git a/pkg/tsdb/cloud-monitoring/httpclient.go b/pkg/tsdb/cloud-monitoring/httpclient.go index 5a57e5f4ac8..aa0ed084194 100644 --- a/pkg/tsdb/cloud-monitoring/httpclient.go +++ b/pkg/tsdb/cloud-monitoring/httpclient.go @@ -23,12 +23,12 @@ type routeInfo struct { var routes = map[string]routeInfo{ cloudMonitor: { method: "GET", - url: "https://monitoring.googleapis.com", + url: "https://monitoring.", scopes: []string{cloudMonitorScope}, }, resourceManager: { method: "GET", - url: "https://cloudresourcemanager.googleapis.com", + url: "https://cloudresourcemanager.", scopes: []string{resourceManagerScope}, }, } @@ -68,6 +68,13 @@ func getMiddleware(model *datasourceInfo, routePath string) (httpclient.Middlewa return tokenprovider.AuthMiddleware(provider), nil } +func buildURL(route string, universeDomain string) string { + if universeDomain == "" { + universeDomain = "googleapis.com" + } + return routes[route].url + universeDomain +} + func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider *httpclient.Provider, route string) (*http.Client, error) { m, err := getMiddleware(model, route) if err != nil { diff --git a/pkg/tsdb/cloud-monitoring/resource_handler_test.go b/pkg/tsdb/cloud-monitoring/resource_handler_test.go index 5f315ef9a59..64742233453 100644 --- a/pkg/tsdb/cloud-monitoring/resource_handler_test.go +++ b/pkg/tsdb/cloud-monitoring/resource_handler_test.go @@ -111,7 +111,7 @@ func Test_setRequestVariables(t *testing.T) { im: &fakeInstance{ services: map[string]datasourceService{ cloudMonitor: { - url: routes[cloudMonitor].url, + url: buildURL(cloudMonitor, "googleapis.com"), client: &http.Client{}, }, }, diff --git a/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx index d866ad49185..e3ddd378078 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx @@ -1,10 +1,10 @@ import { memo } from 'react'; -import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; +import { DataSourcePluginOptionsEditorProps, updateDatasourcePluginJsonDataOption } from '@grafana/data'; import { ConnectionConfig } from '@grafana/google-sdk'; import { ConfigSection, DataSourceDescription } from '@grafana/plugin-ui'; -import { reportInteraction, config } from '@grafana/runtime'; -import { Divider, SecureSocksProxySettings } from '@grafana/ui'; +import { config, reportInteraction } from '@grafana/runtime'; +import { Divider, Field, Input, SecureSocksProxySettings, Stack } from '@grafana/ui'; import { CloudMonitoringOptions, CloudMonitoringSecureJsonData } from '../../types/types'; @@ -36,14 +36,33 @@ export const ConfigEditor = memo(({ options, onOptionsChange }: Props) => { - + + + + updateDatasourcePluginJsonDataOption( + { options, onOptionsChange }, + 'universeDomain', + event.currentTarget.value + ) + } + placeholder="googleapis.com" + > + + + )} + ); }); diff --git a/public/app/plugins/datasource/cloud-monitoring/types/types.ts b/public/app/plugins/datasource/cloud-monitoring/types/types.ts index 849f72ea69b..f2e17444fb0 100644 --- a/public/app/plugins/datasource/cloud-monitoring/types/types.ts +++ b/public/app/plugins/datasource/cloud-monitoring/types/types.ts @@ -38,6 +38,7 @@ export interface Aggregation { export interface CloudMonitoringOptions extends DataSourceOptions { gceDefaultProject?: string; enableSecureSocksProxy?: boolean; + universeDomain?: string; } export interface CloudMonitoringSecureJsonData extends DataSourceSecureJsonData {}