mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudMonitoring: switch from ApplyRoute to AuthMiddleware (#40787)
This commit is contained in:
parent
2c3b35df64
commit
8ee3afa4c3
@ -23,7 +23,6 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
@ -63,6 +62,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
dsName = "stackdriver"
|
||||||
|
|
||||||
gceAuthentication string = "gce"
|
gceAuthentication string = "gce"
|
||||||
jwtAuthentication string = "jwt"
|
jwtAuthentication string = "jwt"
|
||||||
metricQueryType string = "metrics"
|
metricQueryType string = "metrics"
|
||||||
@ -87,7 +88,7 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pl
|
|||||||
QueryDataHandler: s,
|
QueryDataHandler: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := s.backendPluginManager.Register("stackdriver", factory); err != nil {
|
if err := s.backendPluginManager.Register(dsName, factory); err != nil {
|
||||||
slog.Error("Failed to register plugin", "error", err)
|
slog.Error("Failed to register plugin", "error", err)
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
@ -112,9 +113,10 @@ type datasourceInfo struct {
|
|||||||
url string
|
url string
|
||||||
authenticationType string
|
authenticationType string
|
||||||
defaultProject string
|
defaultProject string
|
||||||
|
clientEmail string
|
||||||
|
tokenUri string
|
||||||
client *http.Client
|
client *http.Client
|
||||||
|
|
||||||
jsonData map[string]interface{}
|
|
||||||
decryptedSecureJSONData map[string]string
|
decryptedSecureJSONData map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,16 +128,6 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
|
|||||||
return nil, fmt.Errorf("error reading settings: %w", err)
|
return nil, fmt.Errorf("error reading settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts, err := settings.HTTPClientOptions()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := httpClientProvider.New(opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
authType := jwtAuthentication
|
authType := jwtAuthentication
|
||||||
if authTypeOverride, ok := jsonData["authenticationType"].(string); ok && authTypeOverride != "" {
|
if authTypeOverride, ok := jsonData["authenticationType"].(string); ok && authTypeOverride != "" {
|
||||||
authType = authTypeOverride
|
authType = authTypeOverride
|
||||||
@ -146,16 +138,38 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
|
|||||||
defaultProject = jsonData["defaultProject"].(string)
|
defaultProject = jsonData["defaultProject"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &datasourceInfo{
|
var clientEmail string
|
||||||
|
if jsonData["clientEmail"] != nil {
|
||||||
|
clientEmail = jsonData["clientEmail"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenUri string
|
||||||
|
if jsonData["tokenUri"] != nil {
|
||||||
|
tokenUri = jsonData["tokenUri"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsInfo := &datasourceInfo{
|
||||||
id: settings.ID,
|
id: settings.ID,
|
||||||
updated: settings.Updated,
|
updated: settings.Updated,
|
||||||
url: settings.URL,
|
url: settings.URL,
|
||||||
authenticationType: authType,
|
authenticationType: authType,
|
||||||
defaultProject: defaultProject,
|
defaultProject: defaultProject,
|
||||||
client: client,
|
clientEmail: clientEmail,
|
||||||
jsonData: jsonData,
|
tokenUri: tokenUri,
|
||||||
decryptedSecureJSONData: settings.DecryptedSecureJSONData,
|
decryptedSecureJSONData: settings.DecryptedSecureJSONData,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
opts, err := settings.HTTPClientOptions()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dsInfo.client, err = newHTTPClient(dsInfo, opts, httpClientProvider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dsInfo, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,14 +354,6 @@ func (s *Service) buildQueryExecutors(req *backend.QueryDataRequest) ([]cloudMon
|
|||||||
return cloudMonitoringQueryExecutors, nil
|
return cloudMonitoringQueryExecutors, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reverse(s string) string {
|
|
||||||
chars := []rune(s)
|
|
||||||
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
chars[i], chars[j] = chars[j], chars[i]
|
|
||||||
}
|
|
||||||
return string(chars)
|
|
||||||
}
|
|
||||||
|
|
||||||
func interpolateFilterWildcards(value string) string {
|
func interpolateFilterWildcards(value string) string {
|
||||||
matches := strings.Count(value, "*")
|
matches := strings.Count(value, "*")
|
||||||
switch {
|
switch {
|
||||||
@ -478,19 +484,6 @@ func calculateAlignmentPeriod(alignmentPeriod string, intervalMs int64, duration
|
|||||||
return alignmentPeriod
|
return alignmentPeriod
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSnakeCase(str string) string {
|
|
||||||
return strings.ToLower(matchAllCap.ReplaceAllString(str, "${1}_${2}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsLabel(labels []string, newLabel string) bool {
|
|
||||||
for _, val := range labels {
|
|
||||||
if val == newLabel {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string,
|
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string,
|
||||||
additionalLabels map[string]string, query *cloudMonitoringTimeSeriesFilter) string {
|
additionalLabels map[string]string, query *cloudMonitoringTimeSeriesFilter) string {
|
||||||
if query.AliasBy == "" {
|
if query.AliasBy == "" {
|
||||||
@ -589,34 +582,14 @@ func (s *Service) createRequest(ctx context.Context, pluginCtx backend.PluginCon
|
|||||||
if body != nil {
|
if body != nil {
|
||||||
method = http.MethodPost
|
method = http.MethodPost
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(method, "https://monitoring.googleapis.com/", body)
|
req, err := http.NewRequest(method, cloudMonitoringRoute.url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to create request", "error", err)
|
slog.Error("Failed to create request", "error", err)
|
||||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.URL.Path = proxyPass
|
||||||
// find plugin
|
|
||||||
plugin := s.pluginManager.GetDataSource(pluginCtx.PluginID)
|
|
||||||
if plugin == nil {
|
|
||||||
return nil, errors.New("unable to find datasource plugin CloudMonitoring")
|
|
||||||
}
|
|
||||||
|
|
||||||
var cloudMonitoringRoute *plugins.AppPluginRoute
|
|
||||||
for _, route := range plugin.Routes {
|
|
||||||
if route.Path == "cloudmonitoring" {
|
|
||||||
cloudMonitoringRoute = route
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginproxy.ApplyRoute(ctx, req, proxyPass, cloudMonitoringRoute, pluginproxy.DSInfo{
|
|
||||||
ID: dsInfo.id,
|
|
||||||
Updated: dsInfo.updated,
|
|
||||||
JSONData: dsInfo.jsonData,
|
|
||||||
DecryptedSecureJSONData: dsInfo.decryptedSecureJSONData,
|
|
||||||
}, s.cfg)
|
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
56
pkg/tsdb/cloudmonitoring/httpclient.go
Normal file
56
pkg/tsdb/cloudmonitoring/httpclient.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package cloudmonitoring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-google-sdk-go/pkg/tokenprovider"
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||||
|
infrahttp "github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cloudMonitoringRoute = struct {
|
||||||
|
path string
|
||||||
|
method string
|
||||||
|
url string
|
||||||
|
scopes []string
|
||||||
|
}{
|
||||||
|
path: "cloudmonitoring",
|
||||||
|
method: "GET",
|
||||||
|
url: "https://monitoring.googleapis.com",
|
||||||
|
scopes: []string{"https://www.googleapis.com/auth/monitoring.read"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMiddleware(model *datasourceInfo) (httpclient.Middleware, error) {
|
||||||
|
providerConfig := tokenprovider.Config{
|
||||||
|
RoutePath: cloudMonitoringRoute.path,
|
||||||
|
RouteMethod: cloudMonitoringRoute.method,
|
||||||
|
DataSourceID: model.id,
|
||||||
|
DataSourceUpdated: model.updated,
|
||||||
|
Scopes: cloudMonitoringRoute.scopes,
|
||||||
|
}
|
||||||
|
|
||||||
|
var provider tokenprovider.TokenProvider
|
||||||
|
switch model.authenticationType {
|
||||||
|
case gceAuthentication:
|
||||||
|
provider = tokenprovider.NewGceAccessTokenProvider(providerConfig)
|
||||||
|
case jwtAuthentication:
|
||||||
|
providerConfig.JwtTokenConfig = &tokenprovider.JwtTokenConfig{
|
||||||
|
Email: model.clientEmail,
|
||||||
|
URI: model.tokenUri,
|
||||||
|
PrivateKey: []byte(model.decryptedSecureJSONData["privateKey"]),
|
||||||
|
}
|
||||||
|
provider = tokenprovider.NewJwtAccessTokenProvider(providerConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenprovider.AuthMiddleware(provider), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider infrahttp.Provider) (*http.Client, error) {
|
||||||
|
m, err := getMiddleware(model)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Middlewares = append(opts.Middlewares, m)
|
||||||
|
return clientProvider.New(opts)
|
||||||
|
}
|
@ -29,7 +29,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context
|
|||||||
slog.Info("No project name set on query, using project name from datasource", "projectName", projectName)
|
slog.Info("No project name set on query, using project name from datasource", "projectName", projectName)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := s.createRequest(ctx, req.PluginContext, &dsInfo, path.Join("cloudmonitoringv3/projects", projectName, "timeSeries"), nil)
|
r, err := s.createRequest(ctx, req.PluginContext, &dsInfo, path.Join("/v3/projects", projectName, "timeSeries"), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dr.Error = err
|
dr.Error = err
|
||||||
return dr, cloudMonitoringResponse{}, "", nil
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
|
@ -49,7 +49,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, r
|
|||||||
dr.Error = err
|
dr.Error = err
|
||||||
return dr, cloudMonitoringResponse{}, "", nil
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
}
|
}
|
||||||
r, err := s.createRequest(ctx, req.PluginContext, &dsInfo, path.Join("cloudmonitoringv3/projects", projectName, "timeSeries:query"), bytes.NewBuffer(buf))
|
r, err := s.createRequest(ctx, req.PluginContext, &dsInfo, path.Join("/v3/projects", projectName, "timeSeries:query"), bytes.NewBuffer(buf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dr.Error = err
|
dr.Error = err
|
||||||
return dr, cloudMonitoringResponse{}, "", nil
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
|
26
pkg/tsdb/cloudmonitoring/utils.go
Normal file
26
pkg/tsdb/cloudmonitoring/utils.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cloudmonitoring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reverse(s string) string {
|
||||||
|
chars := []rune(s)
|
||||||
|
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
chars[i], chars[j] = chars[j], chars[i]
|
||||||
|
}
|
||||||
|
return string(chars)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSnakeCase(str string) string {
|
||||||
|
return strings.ToLower(matchAllCap.ReplaceAllString(str, "${1}_${2}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsLabel(labels []string, newLabel string) bool {
|
||||||
|
for _, val := range labels {
|
||||||
|
if val == newLabel {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user