From d61530941a04575e787ed63725b521217411e353 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 9 Sep 2024 10:29:35 +0100 Subject: [PATCH 01/18] AzureMonitor: Add errorsource (#92094) * Add errorsource * Migrate to individually build metric queries * Migrate logs queries to be built individually * Migrate to individually build resource graph queries --- .../azure-log-analytics-datasource.go | 83 +++--- .../azure-log-analytics-datasource_test.go | 4 +- pkg/tsdb/azuremonitor/loganalytics/utils.go | 7 +- .../metrics/azuremonitor-datasource.go | 268 +++++++++--------- .../metrics/azuremonitor-datasource_test.go | 12 +- pkg/tsdb/azuremonitor/metrics/url-builder.go | 8 +- .../azure-resource-graph-datasource.go | 75 +++-- .../azure-resource-graph-datasource_test.go | 28 +- pkg/tsdb/azuremonitor/time/time-grain.go | 3 +- 9 files changed, 246 insertions(+), 242 deletions(-) diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 86c9f4cf3b0..6bb42bf7e54 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -6,6 +6,7 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -18,6 +19,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -136,15 +138,16 @@ func (e *AzureLogAnalyticsDatasource) GetBasicLogsUsage(ctx context.Context, url // 3. parses the responses for each query into data frames func (e *AzureLogAnalyticsDatasource) ExecuteTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, url string, fromAlert bool) (*backend.QueryDataResponse, error) { result := backend.NewQueryDataResponse() - queries, err := e.buildQueries(ctx, originalQueries, dsInfo, fromAlert) - if err != nil { - return nil, err - } - for _, query := range queries { - res, err := e.executeQuery(ctx, query, dsInfo, client, url) + for _, query := range originalQueries { + logsQuery, err := e.buildQuery(ctx, query, dsInfo, fromAlert) if err != nil { - result.Responses[query.RefID] = backend.DataResponse{Error: err} + errorsource.AddErrorToResponse(query.RefID, result, err) + continue + } + res, err := e.executeQuery(ctx, logsQuery, dsInfo, client, url) + if err != nil { + errorsource.AddErrorToResponse(query.RefID, result, err) continue } result.Responses[query.RefID] = *res @@ -179,6 +182,7 @@ func buildLogAnalyticsQuery(query backend.DataQuery, dsInfo types.DatasourceInfo if basicLogsQueryFlag { if meetsBasicLogsCriteria, meetsBasicLogsCriteriaErr := meetsBasicLogsCriteria(resources, fromAlert); meetsBasicLogsCriteriaErr != nil { + // This error is a downstream error return nil, meetsBasicLogsCriteriaErr } else { basicLogsQuery = meetsBasicLogsCriteria @@ -224,45 +228,52 @@ func buildLogAnalyticsQuery(query backend.DataQuery, dsInfo types.DatasourceInfo }, nil } -func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, queries []backend.DataQuery, dsInfo types.DatasourceInfo, fromAlert bool) ([]*AzureLogAnalyticsQuery, error) { - azureLogAnalyticsQueries := []*AzureLogAnalyticsQuery{} +func (e *AzureLogAnalyticsDatasource) buildQuery(ctx context.Context, query backend.DataQuery, dsInfo types.DatasourceInfo, fromAlert bool) (*AzureLogAnalyticsQuery, error) { + var azureLogAnalyticsQuery *AzureLogAnalyticsQuery appInsightsRegExp, err := regexp.Compile("(?i)providers/microsoft.insights/components") if err != nil { return nil, fmt.Errorf("failed to compile Application Insights regex") } - for _, query := range queries { - if query.QueryType == string(dataquery.AzureQueryTypeAzureLogAnalytics) { - azureLogAnalyticsQuery, err := buildLogAnalyticsQuery(query, dsInfo, appInsightsRegExp, fromAlert) - if err != nil { - return nil, fmt.Errorf("failed to build azure log analytics query: %w", err) + if query.QueryType == string(dataquery.AzureQueryTypeAzureLogAnalytics) { + azureLogAnalyticsQuery, err = buildLogAnalyticsQuery(query, dsInfo, appInsightsRegExp, fromAlert) + if err != nil { + errorMessage := fmt.Errorf("failed to build azure log analytics query: %w", err) + var sourceError errorsource.Error + if errors.As(err, &sourceError) { + return nil, errorsource.SourceError(sourceError.Source(), errorMessage, false) } - azureLogAnalyticsQueries = append(azureLogAnalyticsQueries, azureLogAnalyticsQuery) - } - - if query.QueryType == string(dataquery.AzureQueryTypeAzureTraces) || query.QueryType == string(dataquery.AzureQueryTypeTraceql) { - if query.QueryType == string(dataquery.AzureQueryTypeTraceql) { - cfg := backend.GrafanaConfigFromContext(ctx) - hasPromExemplarsToggle := cfg.FeatureToggles().IsEnabled("azureMonitorPrometheusExemplars") - if !hasPromExemplarsToggle { - return nil, fmt.Errorf("query type unsupported as azureMonitorPrometheusExemplars feature toggle is not enabled") - } - } - azureAppInsightsQuery, err := buildAppInsightsQuery(ctx, query, dsInfo, appInsightsRegExp, e.Logger) - if err != nil { - return nil, fmt.Errorf("failed to build azure application insights query: %w", err) - } - azureLogAnalyticsQueries = append(azureLogAnalyticsQueries, azureAppInsightsQuery) + return nil, errorMessage } } - return azureLogAnalyticsQueries, nil + if query.QueryType == string(dataquery.AzureQueryTypeAzureTraces) || query.QueryType == string(dataquery.AzureQueryTypeTraceql) { + if query.QueryType == string(dataquery.AzureQueryTypeTraceql) { + cfg := backend.GrafanaConfigFromContext(ctx) + hasPromExemplarsToggle := cfg.FeatureToggles().IsEnabled("azureMonitorPrometheusExemplars") + if !hasPromExemplarsToggle { + return nil, errorsource.DownstreamError(fmt.Errorf("query type unsupported as azureMonitorPrometheusExemplars feature toggle is not enabled"), false) + } + } + azureAppInsightsQuery, err := buildAppInsightsQuery(ctx, query, dsInfo, appInsightsRegExp, e.Logger) + if err != nil { + errorMessage := fmt.Errorf("failed to build azure application insights query: %w", err) + var sourceError errorsource.Error + if errors.As(err, &sourceError) { + return nil, errorsource.SourceError(sourceError.Source(), errorMessage, false) + } + return nil, errorMessage + } + azureLogAnalyticsQuery = azureAppInsightsQuery + } + + return azureLogAnalyticsQuery, nil } func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *AzureLogAnalyticsQuery, dsInfo types.DatasourceInfo, client *http.Client, url string) (*backend.DataResponse, error) { // If azureLogAnalyticsSameAs is defined and set to false, return an error if sameAs, ok := dsInfo.JSONData["azureLogAnalyticsSameAs"]; ok && !sameAs.(bool) { - return nil, fmt.Errorf("credentials for Log Analytics are no longer supported. Go to the data source configuration to update Azure Monitor credentials") + return nil, errorsource.DownstreamError(fmt.Errorf("credentials for Log Analytics are no longer supported. Go to the data source configuration to update Azure Monitor credentials"), false) } queryJSONModel := dataquery.AzureMonitorQuery{} @@ -273,7 +284,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A if query.QueryType == dataquery.AzureQueryTypeAzureTraces { if query.ResultFormat == dataquery.ResultFormatTrace && query.Query == "" { - return nil, fmt.Errorf("cannot visualise trace events using the trace visualiser") + return nil, errorsource.DownstreamError(fmt.Errorf("cannot visualise trace events using the trace visualiser"), false) } } @@ -294,7 +305,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A res, err := client.Do(req) if err != nil { - return nil, err + return nil, errorsource.DownstreamError(err, false) } defer func() { @@ -611,7 +622,7 @@ func getCorrelationWorkspaces(ctx context.Context, baseResource string, resource }() if res.StatusCode/100 != 2 { - return AzureCorrelationAPIResponse{}, fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)) + return AzureCorrelationAPIResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) } var data AzureCorrelationAPIResponse d := json.NewDecoder(bytes.NewReader(body)) @@ -675,7 +686,7 @@ func (e *AzureLogAnalyticsDatasource) unmarshalResponse(res *http.Response) (Azu }() if res.StatusCode/100 != 2 { - return AzureLogAnalyticsResponse{}, fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)) + return AzureLogAnalyticsResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) } var data AzureLogAnalyticsResponse diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go index a02ea2046eb..a667e9aed0c 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go @@ -741,7 +741,7 @@ func Test_exemplarsFeatureToggle(t *testing.T) { QueryType: string(dataquery.AzureQueryTypeTraceql), } - _, err := ds.buildQueries(ctx, []backend.DataQuery{query}, dsInfo, false) + _, err := ds.buildQuery(ctx, query, dsInfo, false) require.NoError(t, err) }) @@ -761,7 +761,7 @@ func Test_exemplarsFeatureToggle(t *testing.T) { QueryType: string(dataquery.AzureQueryTypeTraceql), } - _, err := ds.buildQueries(ctx, []backend.DataQuery{query}, dsInfo, false) + _, err := ds.buildQuery(ctx, query, dsInfo, false) require.Error(t, err, "query type unsupported as azureMonitorPrometheusExemplars feature toggle is not enabled") }) diff --git a/pkg/tsdb/azuremonitor/loganalytics/utils.go b/pkg/tsdb/azuremonitor/loganalytics/utils.go index 5f1a91f2433..f0802a21748 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/utils.go +++ b/pkg/tsdb/azuremonitor/loganalytics/utils.go @@ -8,6 +8,7 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery" ) @@ -45,14 +46,14 @@ func AddConfigLinks(frame data.Frame, dl string, title *string) data.Frame { // 4. number of selected resources is exactly one func meetsBasicLogsCriteria(resources []string, fromAlert bool) (bool, error) { if fromAlert { - return false, fmt.Errorf("basic Logs queries cannot be used for alerts") + return false, errorsource.DownstreamError(fmt.Errorf("basic Logs queries cannot be used for alerts"), false) } if len(resources) != 1 { - return false, fmt.Errorf("basic logs queries cannot be run against multiple resources") + return false, errorsource.DownstreamError(fmt.Errorf("basic logs queries cannot be run against multiple resources"), false) } if !strings.Contains(strings.ToLower(resources[0]), "microsoft.operationalinsights/workspaces") { - return false, fmt.Errorf("basic Logs queries may only be run against Log Analytics workspaces") + return false, errorsource.DownstreamError(fmt.Errorf("basic Logs queries may only be run against Log Analytics workspaces"), false) } return true, nil diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index 0411019e5e9..8c85239f9d6 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -51,15 +52,15 @@ func (e *AzureMonitorDatasource) ResourceRequest(rw http.ResponseWriter, req *ht func (e *AzureMonitorDatasource) ExecuteTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, url string, fromAlert bool) (*backend.QueryDataResponse, error) { result := backend.NewQueryDataResponse() - queries, err := e.buildQueries(originalQueries, dsInfo) - if err != nil { - return nil, err - } - - for _, query := range queries { - res, err := e.executeQuery(ctx, query, dsInfo, client, url) + for _, query := range originalQueries { + azureQuery, err := e.buildQuery(query, dsInfo) if err != nil { - result.Responses[query.RefID] = backend.DataResponse{Error: err} + errorsource.AddErrorToResponse(query.RefID, result, err) + continue + } + res, err := e.executeQuery(ctx, azureQuery, dsInfo, client, url) + if err != nil { + errorsource.AddErrorToResponse(query.RefID, result, err) continue } result.Responses[query.RefID] = *res @@ -68,151 +69,146 @@ func (e *AzureMonitorDatasource) ExecuteTimeSeriesQuery(ctx context.Context, ori return result, nil } -func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInfo types.DatasourceInfo) ([]*types.AzureMonitorQuery, error) { - azureMonitorQueries := []*types.AzureMonitorQuery{} +func (e *AzureMonitorDatasource) buildQuery(query backend.DataQuery, dsInfo types.DatasourceInfo) (*types.AzureMonitorQuery, error) { + var target string + queryJSONModel := dataquery.AzureMonitorQuery{} + err := json.Unmarshal(query.JSON, &queryJSONModel) + if err != nil { + return nil, fmt.Errorf("failed to decode the Azure Monitor query object from JSON: %w", err) + } - for _, query := range queries { - var target string - queryJSONModel := dataquery.AzureMonitorQuery{} - err := json.Unmarshal(query.JSON, &queryJSONModel) + azJSONModel := queryJSONModel.AzureMonitor + // Legacy: If only MetricDefinition is set, use it as namespace + if azJSONModel.MetricDefinition != nil && *azJSONModel.MetricDefinition != "" && + azJSONModel.MetricNamespace != nil && *azJSONModel.MetricNamespace == "" { + azJSONModel.MetricNamespace = azJSONModel.MetricDefinition + } + + azJSONModel.DimensionFilters = MigrateDimensionFilters(azJSONModel.DimensionFilters) + + alias := "" + if azJSONModel.Alias != nil { + alias = *azJSONModel.Alias + } + azureURL := "" + if queryJSONModel.Subscription != nil { + azureURL = BuildSubscriptionMetricsURL(*queryJSONModel.Subscription) + } + filterInBody := true + resourceIDs := []string{} + resourceMap := map[string]dataquery.AzureMonitorResource{} + if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne { + ub := urlBuilder{ + ResourceURI: azJSONModel.ResourceUri, + // Alternative, used to reconstruct resource URI if it's not present + DefaultSubscription: &dsInfo.Settings.SubscriptionId, + Subscription: queryJSONModel.Subscription, + ResourceGroup: resourceGroup, + MetricNamespace: azJSONModel.MetricNamespace, + ResourceName: resourceName, + } + + // Construct the resourceURI (for legacy query objects pre Grafana 9) + resourceUri, err := ub.buildResourceURI() if err != nil { - return nil, fmt.Errorf("failed to decode the Azure Monitor query object from JSON: %w", err) + return nil, err } - azJSONModel := queryJSONModel.AzureMonitor - // Legacy: If only MetricDefinition is set, use it as namespace - if azJSONModel.MetricDefinition != nil && *azJSONModel.MetricDefinition != "" && - azJSONModel.MetricNamespace != nil && *azJSONModel.MetricNamespace == "" { - azJSONModel.MetricNamespace = azJSONModel.MetricDefinition + // POST requests are only supported at the subscription level + filterInBody = false + if resourceUri != nil { + azureURL = fmt.Sprintf("%s/providers/microsoft.insights/metrics", *resourceUri) + resourceMap[*resourceUri] = dataquery.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName} } - - azJSONModel.DimensionFilters = MigrateDimensionFilters(azJSONModel.DimensionFilters) - - alias := "" - if azJSONModel.Alias != nil { - alias = *azJSONModel.Alias - } - azureURL := "" - if queryJSONModel.Subscription != nil { - azureURL = BuildSubscriptionMetricsURL(*queryJSONModel.Subscription) - } - filterInBody := true - resourceIDs := []string{} - resourceMap := map[string]dataquery.AzureMonitorResource{} - if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne { + } else { + for _, r := range azJSONModel.Resources { ub := urlBuilder{ - ResourceURI: azJSONModel.ResourceUri, - // Alternative, used to reconstruct resource URI if it's not present DefaultSubscription: &dsInfo.Settings.SubscriptionId, Subscription: queryJSONModel.Subscription, - ResourceGroup: resourceGroup, + ResourceGroup: r.ResourceGroup, MetricNamespace: azJSONModel.MetricNamespace, - ResourceName: resourceName, + ResourceName: r.ResourceName, } - - // Construct the resourceURI (for legacy query objects pre Grafana 9) resourceUri, err := ub.buildResourceURI() if err != nil { return nil, err } - // POST requests are only supported at the subscription level - filterInBody = false if resourceUri != nil { - azureURL = fmt.Sprintf("%s/providers/microsoft.insights/metrics", *resourceUri) - resourceMap[*resourceUri] = dataquery.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName} - } - } else { - for _, r := range azJSONModel.Resources { - ub := urlBuilder{ - DefaultSubscription: &dsInfo.Settings.SubscriptionId, - Subscription: queryJSONModel.Subscription, - ResourceGroup: r.ResourceGroup, - MetricNamespace: azJSONModel.MetricNamespace, - ResourceName: r.ResourceName, - } - resourceUri, err := ub.buildResourceURI() - if err != nil { - return nil, err - } - - if resourceUri != nil { - resourceMap[*resourceUri] = r - } - resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", *resourceUri)) + resourceMap[*resourceUri] = r } + resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", *resourceUri)) } - - // old model - dimension := "" - if azJSONModel.Dimension != nil { - dimension = strings.TrimSpace(*azJSONModel.Dimension) - } - dimensionFilter := "" - if azJSONModel.DimensionFilter != nil { - dimensionFilter = strings.TrimSpace(*azJSONModel.DimensionFilter) - } - - dimSB := strings.Builder{} - - if dimension != "" && dimensionFilter != "" && dimension != "None" && len(azJSONModel.DimensionFilters) == 0 { - dimSB.WriteString(fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter)) - } else { - for i, filter := range azJSONModel.DimensionFilters { - if len(filter.Filters) == 0 { - dimSB.WriteString(fmt.Sprintf("%s eq '*'", *filter.Dimension)) - } else { - dimSB.WriteString(types.ConstructFiltersString(filter)) - } - if i != len(azJSONModel.DimensionFilters)-1 { - dimSB.WriteString(" and ") - } - } - } - - filterString := strings.Join(resourceIDs, " or ") - - if dimSB.String() != "" { - if filterString != "" { - filterString = fmt.Sprintf("(%s) and (%s)", filterString, dimSB.String()) - } else { - filterString = dimSB.String() - } - } - - params, err := getParams(azJSONModel, query) - if err != nil { - return nil, err - } - target = params.Encode() - - sub := "" - if queryJSONModel.Subscription != nil { - sub = *queryJSONModel.Subscription - } - - query := &types.AzureMonitorQuery{ - URL: azureURL, - Target: target, - Params: params, - RefID: query.RefID, - Alias: alias, - TimeRange: query.TimeRange, - Dimensions: azJSONModel.DimensionFilters, - Resources: resourceMap, - Subscription: sub, - } - if filterString != "" { - if filterInBody { - query.BodyFilter = filterString - } else { - query.Params.Add("$filter", filterString) - } - } - azureMonitorQueries = append(azureMonitorQueries, query) } - return azureMonitorQueries, nil + // old model + dimension := "" + if azJSONModel.Dimension != nil { + dimension = strings.TrimSpace(*azJSONModel.Dimension) + } + dimensionFilter := "" + if azJSONModel.DimensionFilter != nil { + dimensionFilter = strings.TrimSpace(*azJSONModel.DimensionFilter) + } + + dimSB := strings.Builder{} + + if dimension != "" && dimensionFilter != "" && dimension != "None" && len(azJSONModel.DimensionFilters) == 0 { + dimSB.WriteString(fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter)) + } else { + for i, filter := range azJSONModel.DimensionFilters { + if len(filter.Filters) == 0 { + dimSB.WriteString(fmt.Sprintf("%s eq '*'", *filter.Dimension)) + } else { + dimSB.WriteString(types.ConstructFiltersString(filter)) + } + if i != len(azJSONModel.DimensionFilters)-1 { + dimSB.WriteString(" and ") + } + } + } + + filterString := strings.Join(resourceIDs, " or ") + + if dimSB.String() != "" { + if filterString != "" { + filterString = fmt.Sprintf("(%s) and (%s)", filterString, dimSB.String()) + } else { + filterString = dimSB.String() + } + } + + params, err := getParams(azJSONModel, query) + if err != nil { + return nil, err + } + target = params.Encode() + + sub := "" + if queryJSONModel.Subscription != nil { + sub = *queryJSONModel.Subscription + } + + azureQuery := &types.AzureMonitorQuery{ + URL: azureURL, + Target: target, + Params: params, + RefID: query.RefID, + Alias: alias, + TimeRange: query.TimeRange, + Dimensions: azJSONModel.DimensionFilters, + Resources: resourceMap, + Subscription: sub, + } + if filterString != "" { + if filterInBody { + azureQuery.BodyFilter = filterString + } else { + azureQuery.Params.Add("$filter", filterString) + } + } + + return azureQuery, nil } func getParams(azJSONModel *dataquery.AzureMetricQuery, query backend.DataQuery) (url.Values, error) { @@ -288,7 +284,7 @@ func (e *AzureMonitorDatasource) retrieveSubscriptionDetails(cli *http.Client, c } if res.StatusCode/100 != 2 { - return "", fmt.Errorf("request failed, status: %s, error: %s", res.Status, string(body)) + return "", errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, error: %s", res.Status, string(body)), false) } var data types.SubscriptionsResponse @@ -325,7 +321,7 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *types. res, err := cli.Do(req) if err != nil { - return nil, err + return nil, errorsource.DownstreamError(err, false) } defer func() { @@ -370,7 +366,7 @@ func (e *AzureMonitorDatasource) unmarshalResponse(res *http.Response) (types.Az } if res.StatusCode/100 != 2 { - return types.AzureMonitorResponse{}, fmt.Errorf("request failed, status: %s, error: %s", res.Status, string(body)) + return types.AzureMonitorResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) } var data types.AzureMonitorResponse diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go index 1e372632be9..e0aa10605d0 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go @@ -294,7 +294,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { }, } - queries, err := datasource.buildQueries(tsdbQuery, dsInfo) + query, err := datasource.buildQuery(tsdbQuery[0], dsInfo) require.NoError(t, err) resources := map[string]dataquery.AzureMonitorResource{} @@ -321,12 +321,12 @@ func TestAzureMonitorBuildQueries(t *testing.T) { Resources: resources, } - assert.Equal(t, tt.expectedParamFilter, queries[0].Params.Get("$filter")) + assert.Equal(t, tt.expectedParamFilter, query.Params.Get("$filter")) if azureMonitorQuery.URL == "" { azureMonitorQuery.URL = "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics" } - if diff := cmp.Diff(azureMonitorQuery, queries[0], cmpopts.IgnoreUnexported(struct{}{}), cmpopts.IgnoreFields(types.AzureMonitorQuery{}, "Params", "Dimensions")); diff != "" { + if diff := cmp.Diff(azureMonitorQuery, query, cmpopts.IgnoreUnexported(struct{}{}), cmpopts.IgnoreFields(types.AzureMonitorQuery{}, "Params", "Dimensions")); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } @@ -338,7 +338,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedPortalURL = *tt.expectedPortalURL } - actual, err := getQueryUrl(queries[0], "http://ds", "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana", "grafana") + actual, err := getQueryUrl(query, "http://ds", "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana", "grafana") require.NoError(t, err) require.Equal(t, expectedPortalURL, actual) }) @@ -359,10 +359,10 @@ func TestCustomNamespace(t *testing.T) { }, } - result, err := datasource.buildQueries(q, types.DatasourceInfo{}) + result, err := datasource.buildQuery(q[0], types.DatasourceInfo{}) require.NoError(t, err) expected := "custom/namespace" - require.Equal(t, expected, result[0].Params.Get("metricnamespace")) + require.Equal(t, expected, result.Params.Get("metricnamespace")) }) } diff --git a/pkg/tsdb/azuremonitor/metrics/url-builder.go b/pkg/tsdb/azuremonitor/metrics/url-builder.go index a7ae29e1fd0..32b268eeca5 100644 --- a/pkg/tsdb/azuremonitor/metrics/url-builder.go +++ b/pkg/tsdb/azuremonitor/metrics/url-builder.go @@ -3,6 +3,8 @@ package metrics import ( "fmt" "strings" + + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" ) // urlBuilder builds the URL for calling the Azure Monitor API @@ -33,7 +35,7 @@ func (params *urlBuilder) buildResourceURI() (*string, error) { if metricNamespace == nil || *metricNamespace == "" { if params.MetricDefinition == nil || *params.MetricDefinition == "" { - return nil, fmt.Errorf("no metricNamespace or metricDefiniton value provided") + return nil, errorsource.DownstreamError(fmt.Errorf("no metricNamespace or metricDefiniton value provided"), false) } metricNamespace = params.MetricDefinition } @@ -45,7 +47,7 @@ func (params *urlBuilder) buildResourceURI() (*string, error) { provider = metricNamespaceArray[0] metricNamespaceArray = metricNamespaceArray[1:] } else { - return nil, fmt.Errorf("metricNamespace is not in the correct format") + return nil, errorsource.DownstreamError(fmt.Errorf("metricNamespace is not in the correct format"), false) } var resourceNameArray []string @@ -76,7 +78,7 @@ func (params *urlBuilder) buildResourceURI() (*string, error) { if i < len(resourceNameArray) { urlArray = append(urlArray, namespace, resourceNameArray[i]) } else { - return nil, fmt.Errorf("resourceNameArray does not have enough elements") + return nil, errorsource.DownstreamError(fmt.Errorf("resourceNameArray does not have enough elements"), false) } } diff --git a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go index 8300a3c423a..d1252ed167c 100644 --- a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go +++ b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go @@ -15,6 +15,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -63,15 +64,14 @@ func (e *AzureResourceGraphDatasource) ExecuteTimeSeriesQuery(ctx context.Contex Responses: map[string]backend.DataResponse{}, } - queries, err := e.buildQueries(originalQueries, dsInfo) - if err != nil { - return nil, err - } - - for _, query := range queries { - res, err := e.executeQuery(ctx, query, dsInfo, client, url) + for _, query := range originalQueries { + graphQuery, err := e.buildQuery(query, dsInfo) if err != nil { - result.Responses[query.RefID] = backend.DataResponse{Error: err} + return nil, err + } + res, err := e.executeQuery(ctx, graphQuery, dsInfo, client, url) + if err != nil { + errorsource.AddErrorToResponse(query.RefID, result, err) continue } result.Responses[query.RefID] = *res @@ -87,38 +87,33 @@ type argJSONQuery struct { } `json:"azureResourceGraph"` } -func (e *AzureResourceGraphDatasource) buildQueries(queries []backend.DataQuery, dsInfo types.DatasourceInfo) ([]*AzureResourceGraphQuery, error) { - azureResourceGraphQueries := make([]*AzureResourceGraphQuery, len(queries)) - for i, query := range queries { - queryJSONModel := argJSONQuery{} - err := json.Unmarshal(query.JSON, &queryJSONModel) - if err != nil { - return nil, fmt.Errorf("failed to decode the Azure Resource Graph query object from JSON: %w", err) - } - - azureResourceGraphTarget := queryJSONModel.AzureResourceGraph - - resultFormat := azureResourceGraphTarget.ResultFormat - if resultFormat == "" { - resultFormat = "table" - } - - interpolatedQuery, err := macros.KqlInterpolate(query, dsInfo, azureResourceGraphTarget.Query) - if err != nil { - return nil, err - } - - azureResourceGraphQueries[i] = &AzureResourceGraphQuery{ - RefID: query.RefID, - ResultFormat: resultFormat, - JSON: query.JSON, - InterpolatedQuery: interpolatedQuery, - TimeRange: query.TimeRange, - QueryType: query.QueryType, - } +func (e *AzureResourceGraphDatasource) buildQuery(query backend.DataQuery, dsInfo types.DatasourceInfo) (*AzureResourceGraphQuery, error) { + queryJSONModel := argJSONQuery{} + err := json.Unmarshal(query.JSON, &queryJSONModel) + if err != nil { + return nil, fmt.Errorf("failed to decode the Azure Resource Graph query object from JSON: %w", err) } - return azureResourceGraphQueries, nil + azureResourceGraphTarget := queryJSONModel.AzureResourceGraph + + resultFormat := azureResourceGraphTarget.ResultFormat + if resultFormat == "" { + resultFormat = "table" + } + + interpolatedQuery, err := macros.KqlInterpolate(query, dsInfo, azureResourceGraphTarget.Query) + if err != nil { + return nil, err + } + + return &AzureResourceGraphQuery{ + RefID: query.RefID, + ResultFormat: resultFormat, + JSON: query.JSON, + InterpolatedQuery: interpolatedQuery, + TimeRange: query.TimeRange, + QueryType: query.QueryType, + }, nil } func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query *AzureResourceGraphQuery, dsInfo types.DatasourceInfo, client *http.Client, dsURL string) (*backend.DataResponse, error) { @@ -164,7 +159,7 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query * res, err := client.Do(req) if err != nil { - return nil, err + return nil, errorsource.DownstreamError(err, false) } defer func() { @@ -224,7 +219,7 @@ func (e *AzureResourceGraphDatasource) unmarshalResponse(res *http.Response) (Az }() if res.StatusCode/100 != 2 { - return AzureResourceGraphResponse{}, fmt.Errorf("%s. Azure Resource Graph error: %s", res.Status, string(body)) + return AzureResourceGraphResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("%s. Azure Resource Graph error: %s", res.Status, string(body)), false) } var data AzureResourceGraphResponse diff --git a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go index 4668bcefbd7..1ab88969217 100644 --- a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go +++ b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go @@ -25,11 +25,11 @@ func TestBuildingAzureResourceGraphQueries(t *testing.T) { fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) tests := []struct { - name string - queryModel []backend.DataQuery - timeRange backend.TimeRange - azureResourceGraphQueries []*AzureResourceGraphQuery - Err require.ErrorAssertionFunc + name string + queryModel []backend.DataQuery + timeRange backend.TimeRange + azureResourceGraphQuery AzureResourceGraphQuery + Err require.ErrorAssertionFunc }{ { name: "Query with macros should be interpolated", @@ -49,20 +49,18 @@ func TestBuildingAzureResourceGraphQueries(t *testing.T) { RefID: "A", }, }, - azureResourceGraphQueries: []*AzureResourceGraphQuery{ - { - RefID: "A", - ResultFormat: "table", - URL: "", - JSON: []byte(`{ + azureResourceGraphQuery: AzureResourceGraphQuery{ + RefID: "A", + ResultFormat: "table", + URL: "", + JSON: []byte(`{ "queryType": "Azure Resource Graph", "azureResourceGraph": { "query": "resources | where $__contains(name,'res1','res2')", "resultFormat": "table" } }`), - InterpolatedQuery: "resources | where ['name'] in ('res1','res2')", - }, + InterpolatedQuery: "resources | where ['name'] in ('res1','res2')", }, Err: require.NoError, }, @@ -70,9 +68,9 @@ func TestBuildingAzureResourceGraphQueries(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - queries, err := datasource.buildQueries(tt.queryModel, types.DatasourceInfo{}) + query, err := datasource.buildQuery(tt.queryModel[0], types.DatasourceInfo{}) tt.Err(t, err) - if diff := cmp.Diff(tt.azureResourceGraphQueries, queries, cmpopts.IgnoreUnexported(struct{}{})); diff != "" { + if diff := cmp.Diff(&tt.azureResourceGraphQuery, query, cmpopts.IgnoreUnexported(struct{}{})); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } }) diff --git a/pkg/tsdb/azuremonitor/time/time-grain.go b/pkg/tsdb/azuremonitor/time/time-grain.go index dfc62d9c1ff..d89e59bbf96 100644 --- a/pkg/tsdb/azuremonitor/time/time-grain.go +++ b/pkg/tsdb/azuremonitor/time/time-grain.go @@ -7,6 +7,7 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" + "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" ) // TimeGrain handles conversions between @@ -26,7 +27,7 @@ func CreateISO8601DurationFromIntervalMS(it int64) (string, error) { timeValueString := formatted[0 : len(formatted)-1] timeValue, err := strconv.Atoi(timeValueString) if err != nil { - return "", fmt.Errorf("could not parse interval %q to an ISO 8061 duration: %w", it, err) + return "", errorsource.DownstreamError(fmt.Errorf("could not parse interval %q to an ISO 8061 duration: %w", it, err), false) } unit := formatted[len(formatted)-1:] From 2c47d246fc15b1a3a0405b5937d1d79ee7ccdff0 Mon Sep 17 00:00:00 2001 From: Will Browne Date: Mon, 9 Sep 2024 10:38:35 +0100 Subject: [PATCH 02/18] Plugins: Introduce `LoadingStrategy` for frontend loading logic (#92392) * do it all * feat(plugins): move loadingStrategy to ds pluginMeta and add to plugin settings endpoint * support child plugins and update tests * use relative path for nested plugins * feat(plugins): support nested plugins in the plugin loader cache by extracting pluginId from path * feat(grafana-data): add plugin loading strategy to plugin meta and export * feat(plugins): pass down loadingStrategy to fe plugin loader * refactor(plugins): make PluginLoadingStrategy an enum * feat(plugins): add the loading strategy to the fe plugin loader cache * feat(plugins): load fe plugin js assets as script tags based on be loadingStrategy * add more tests * feat(plugins): add loading strategy to plugin preloader * feat(plugins): make loadingStrategy a maybe and provide fetch fallback * test(alerting): update config.apps mocks to include loadingStrategy * fix format --------- Co-authored-by: Jack Westbrook --- packages/grafana-data/src/index.ts | 1 + packages/grafana-data/src/types/plugin.ts | 7 + packages/grafana-runtime/src/config.ts | 2 + pkg/api/dtos/plugins.go | 1 + pkg/api/frontendsettings.go | 42 +++-- pkg/api/frontendsettings_test.go | 157 +++++++++++++--- pkg/api/http_server.go | 5 +- pkg/api/plugins.go | 1 + pkg/api/plugins_test.go | 4 + .../manager/loader/assetpath/assetpath.go | 22 ++- .../loader/assetpath/assetpath_test.go | 150 ++++++++++++++++ pkg/plugins/models.go | 46 +++-- .../pluginsintegration/loader/loader_test.go | 12 +- .../pluginassets/pluginassets.go | 81 +++++++++ .../pluginassets/pluginassets_test.go | 168 ++++++++++++++++++ .../pluginsintegration/pluginsintegration.go | 2 + .../pluginsintegration/pluginstore/plugins.go | 13 +- .../unified/mocks/server/handlers/plugins.ts | 3 +- .../alerting/unified/utils/rules.test.ts | 2 + .../app/features/plugins/importPanelPlugin.ts | 4 +- .../app/features/plugins/loader/cache.test.ts | 64 +++++-- public/app/features/plugins/loader/cache.ts | 26 ++- .../app/features/plugins/pluginPreloader.ts | 7 +- public/app/features/plugins/plugin_loader.ts | 23 ++- 24 files changed, 724 insertions(+), 119 deletions(-) create mode 100644 pkg/services/pluginsintegration/pluginassets/pluginassets.go create mode 100644 pkg/services/pluginsintegration/pluginassets/pluginassets_test.go diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 4d7e6ed1201..f7b3fa21399 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -579,6 +579,7 @@ export { PluginSignatureType, PluginErrorCode, PluginIncludeType, + PluginLoadingStrategy, GrafanaPlugin, type PluginError, type AngularMeta, diff --git a/packages/grafana-data/src/types/plugin.ts b/packages/grafana-data/src/types/plugin.ts index c5632e5d69b..56ec6168d0b 100644 --- a/packages/grafana-data/src/types/plugin.ts +++ b/packages/grafana-data/src/types/plugin.ts @@ -59,6 +59,12 @@ export interface AngularMeta { hideDeprecation: boolean; } +// Signals to SystemJS how to load frontend js assets. +export enum PluginLoadingStrategy { + fetch = 'fetch', + script = 'script', +} + export interface PluginMeta { id: string; name: string; @@ -91,6 +97,7 @@ export interface PluginMeta { live?: boolean; angular?: AngularMeta; angularDetected?: boolean; + loadingStrategy?: PluginLoadingStrategy; } interface PluginDependencyInfo { diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index f92f4023bf0..8c1c1c93d73 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -17,6 +17,7 @@ import { SystemDateFormatSettings, getThemeById, AngularMeta, + PluginLoadingStrategy, } from '@grafana/data'; export interface AzureSettings { @@ -40,6 +41,7 @@ export type AppPluginConfig = { version: string; preload: boolean; angular: AngularMeta; + loadingStrategy: PluginLoadingStrategy; }; export type PreinstalledPlugin = { diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 7d768e059e4..0d882bce28f 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -28,6 +28,7 @@ type PluginSetting struct { SignatureType plugins.SignatureType `json:"signatureType"` SignatureOrg string `json:"signatureOrg"` AngularDetected bool `json:"angularDetected"` + LoadingStrategy plugins.LoadingStrategy `json:"loadingStrategy"` } type PluginListItem struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 677e8fa98aa..7d50bd47140 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -110,7 +110,8 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro apps := make(map[string]*plugins.AppDTO, 0) for _, ap := range availablePlugins[plugins.TypeApp] { - apps[ap.Plugin.ID] = newAppDTO( + apps[ap.Plugin.ID] = hs.newAppDTO( + c.Req.Context(), ap.Plugin, ap.Settings, ) @@ -140,18 +141,19 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro } panels[panel.ID] = plugins.PanelDTO{ - ID: panel.ID, - Name: panel.Name, - AliasIDs: panel.AliasIDs, - Info: panel.Info, - Module: panel.Module, - BaseURL: panel.BaseURL, - SkipDataQuery: panel.SkipDataQuery, - HideFromList: panel.HideFromList, - ReleaseState: string(panel.State), - Signature: string(panel.Signature), - Sort: getPanelSort(panel.ID), - Angular: panel.Angular, + ID: panel.ID, + Name: panel.Name, + AliasIDs: panel.AliasIDs, + Info: panel.Info, + Module: panel.Module, + BaseURL: panel.BaseURL, + SkipDataQuery: panel.SkipDataQuery, + HideFromList: panel.HideFromList, + ReleaseState: string(panel.State), + Signature: string(panel.Signature), + Sort: getPanelSort(panel.ID), + Angular: panel.Angular, + LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), panel), } } @@ -455,6 +457,7 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug BaseURL: plugin.BaseURL, Angular: plugin.Angular, MultiValueFilterOperators: plugin.MultiValueFilterOperators, + LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), plugin), } if ds.JsonData == nil { @@ -551,13 +554,14 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug return dataSources, nil } -func newAppDTO(plugin pluginstore.Plugin, settings pluginsettings.InfoDTO) *plugins.AppDTO { +func (hs *HTTPServer) newAppDTO(ctx context.Context, plugin pluginstore.Plugin, settings pluginsettings.InfoDTO) *plugins.AppDTO { app := &plugins.AppDTO{ - ID: plugin.ID, - Version: plugin.Info.Version, - Path: plugin.Module, - Preload: false, - Angular: plugin.Angular, + ID: plugin.ID, + Version: plugin.Info.Version, + Path: plugin.Module, + Preload: false, + Angular: plugin.Angular, + LoadingStrategy: hs.pluginAssets.LoadingStrategy(ctx, plugin), } if settings.Enabled { diff --git a/pkg/api/frontendsettings_test.go b/pkg/api/frontendsettings_test.go index ad5a753d7a7..6d7139e28db 100644 --- a/pkg/api/frontendsettings_test.go +++ b/pkg/api/frontendsettings_test.go @@ -25,6 +25,7 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/rendering" @@ -35,7 +36,7 @@ import ( "github.com/grafana/grafana/pkg/web" ) -func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.FeatureToggles, pstore pluginstore.Store, psettings pluginsettings.Service) (*web.Mux, *HTTPServer) { +func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.FeatureToggles, pstore pluginstore.Store, psettings pluginsettings.Service, passets *pluginassets.Service) (*web.Mux, *HTTPServer) { t.Helper() db.InitTestDB(t) // nolint:staticcheck @@ -50,6 +51,11 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F }) } + pluginsCDN := pluginscdn.ProvideService(&config.PluginManagementCfg{ + PluginsCDNURLTemplate: cfg.PluginsCDNURLTemplate, + PluginSettings: cfg.PluginSettings, + }) + var pluginStore = pstore if pluginStore == nil { pluginStore = &pluginstore.FakePluginStore{} @@ -60,6 +66,11 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F pluginsSettings = &pluginsettings.FakePluginSettings{} } + var pluginsAssets = passets + if pluginsAssets == nil { + pluginsAssets = pluginassets.ProvideService(cfg, pluginsCDN) + } + hs := &HTTPServer{ authnService: &authntest.FakeService{}, Cfg: cfg, @@ -69,16 +80,14 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F Cfg: cfg, RendererPluginManager: &fakeRendererPluginManager{}, }, - SQLStore: db.InitTestDB(t), - SettingsProvider: setting.ProvideProvider(cfg), - pluginStore: pluginStore, - grafanaUpdateChecker: &updatechecker.GrafanaService{}, - AccessControl: accesscontrolmock.New(), - PluginSettings: pluginsSettings, - pluginsCDNService: pluginscdn.ProvideService(&config.PluginManagementCfg{ - PluginsCDNURLTemplate: cfg.PluginsCDNURLTemplate, - PluginSettings: cfg.PluginSettings, - }), + SQLStore: db.InitTestDB(t), + SettingsProvider: setting.ProvideProvider(cfg), + pluginStore: pluginStore, + grafanaUpdateChecker: &updatechecker.GrafanaService{}, + AccessControl: accesscontrolmock.New(), + PluginSettings: pluginsSettings, + pluginsCDNService: pluginsCDN, + pluginAssets: pluginsAssets, namespacer: request.GetNamespaceMapper(cfg), SocialService: socialimpl.ProvideService(cfg, features, &usagestats.UsageStatsMock{}, supportbundlestest.NewFakeBundleService(), remotecache.NewFakeCacheStorage(), nil, &ssosettingstests.MockService{}), managedPluginsService: managedplugins.NewNoop(), @@ -108,7 +117,7 @@ func TestHTTPServer_GetFrontendSettings_hideVersionAnonymous(t *testing.T) { cfg.BuildVersion = "7.8.9" cfg.BuildCommit = "01234567" - m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil) + m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) @@ -198,7 +207,7 @@ func TestHTTPServer_GetFrontendSettings_pluginsCDNBaseURL(t *testing.T) { if test.mutateCfg != nil { test.mutateCfg(cfg) } - m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil) + m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) recorder := httptest.NewRecorder() @@ -221,6 +230,7 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) { desc string pluginStore func() pluginstore.Store pluginSettings func() pluginsettings.Service + pluginAssets func() *pluginassets.Service expected settings }{ { @@ -245,13 +255,17 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", false), } }, + pluginAssets: func() *pluginassets.Service { + return pluginassets.ProvideService(setting.NewCfg(), pluginscdn.ProvideService(&config.PluginManagementCfg{})) + }, expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { - ID: "test-app", - Preload: false, - Path: "/test-app/module.js", - Version: "0.5.0", + ID: "test-app", + Preload: false, + Path: "/test-app/module.js", + Version: "0.5.0", + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -278,13 +292,17 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, + pluginAssets: func() *pluginassets.Service { + return pluginassets.ProvideService(setting.NewCfg(), pluginscdn.ProvideService(&config.PluginManagementCfg{})) + }, expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { - ID: "test-app", - Preload: true, - Path: "/test-app/module.js", - Version: "0.5.0", + ID: "test-app", + Preload: true, + Path: "/test-app/module.js", + Version: "0.5.0", + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -312,14 +330,99 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, + pluginAssets: func() *pluginassets.Service { + return pluginassets.ProvideService(setting.NewCfg(), pluginscdn.ProvideService(&config.PluginManagementCfg{})) + }, expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { - ID: "test-app", - Preload: true, - Path: "/test-app/module.js", - Version: "0.5.0", - Angular: plugins.AngularMeta{Detected: true}, + ID: "test-app", + Preload: true, + Path: "/test-app/module.js", + Version: "0.5.0", + Angular: plugins.AngularMeta{Detected: true}, + LoadingStrategy: plugins.LoadingStrategyFetch, + }, + }, + }, + }, + { + desc: "app plugin with create plugin version compatible with script loading strategy", + pluginStore: func() pluginstore.Store { + return &pluginstore.FakePluginStore{ + PluginList: []pluginstore.Plugin{ + { + Module: fmt.Sprintf("/%s/module.js", "test-app"), + JSONData: plugins.JSONData{ + ID: "test-app", + Info: plugins.Info{Version: "0.5.0"}, + Type: plugins.TypeApp, + Preload: true, + }, + }, + }, + } + }, + pluginSettings: func() pluginsettings.Service { + return &pluginsettings.FakePluginSettings{ + Plugins: newAppSettings("test-app", true), + } + }, + pluginAssets: func() *pluginassets.Service { + return pluginassets.ProvideService(&setting.Cfg{ + PluginSettings: map[string]map[string]string{ + "test-app": { + pluginassets.CreatePluginVersionCfgKey: pluginassets.CreatePluginVersionScriptSupportEnabled, + }, + }, + }, pluginscdn.ProvideService(&config.PluginManagementCfg{})) + }, + expected: settings{ + Apps: map[string]*plugins.AppDTO{ + "test-app": { + ID: "test-app", + Preload: true, + Path: "/test-app/module.js", + Version: "0.5.0", + LoadingStrategy: plugins.LoadingStrategyScript, + }, + }, + }, + }, + { + desc: "app plugin with CDN class", + pluginStore: func() pluginstore.Store { + return &pluginstore.FakePluginStore{ + PluginList: []pluginstore.Plugin{ + { + Class: plugins.ClassCDN, + Module: fmt.Sprintf("/%s/module.js", "test-app"), + JSONData: plugins.JSONData{ + ID: "test-app", + Info: plugins.Info{Version: "0.5.0"}, + Type: plugins.TypeApp, + Preload: true, + }, + }, + }, + } + }, + pluginSettings: func() pluginsettings.Service { + return &pluginsettings.FakePluginSettings{ + Plugins: newAppSettings("test-app", true), + } + }, + pluginAssets: func() *pluginassets.Service { + return pluginassets.ProvideService(setting.NewCfg(), pluginscdn.ProvideService(&config.PluginManagementCfg{})) + }, + expected: settings{ + Apps: map[string]*plugins.AppDTO{ + "test-app": { + ID: "test-app", + Preload: true, + Path: "/test-app/module.js", + Version: "0.5.0", + LoadingStrategy: plugins.LoadingStrategyFetch, }, }, }, @@ -329,7 +432,7 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { cfg := setting.NewCfg() - m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), test.pluginSettings()) + m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), test.pluginSettings(), test.pluginAssets()) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) recorder := httptest.NewRecorder() diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 6f1295aa48f..fa13e721a94 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -78,6 +78,7 @@ import ( "github.com/grafana/grafana/pkg/services/playlist" "github.com/grafana/grafana/pkg/services/plugindashboards" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" @@ -146,6 +147,7 @@ type HTTPServer struct { pluginDashboardService plugindashboards.Service pluginStaticRouteResolver plugins.StaticRouteResolver pluginErrorResolver plugins.ErrorResolver + pluginAssets *pluginassets.Service SearchService search.Service ShortURLService shorturls.Service QueryHistoryService queryhistory.Service @@ -247,7 +249,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService, pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service, dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore, - serviceaccountsService serviceaccounts.Service, + serviceaccountsService serviceaccounts.Service, pluginAssets *pluginassets.Service, authInfoService login.AuthInfoService, storageService store.StorageService, notificationService notifications.Service, dashboardService dashboards.DashboardService, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service, @@ -286,6 +288,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi pluginStore: pluginStore, pluginStaticRouteResolver: pluginStaticRouteResolver, pluginDashboardService: pluginDashboardService, + pluginAssets: pluginAssets, pluginErrorResolver: pluginErrorResolver, pluginFileStore: pluginFileStore, grafanaUpdateChecker: grafanaUpdateChecker, diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index d1793e727e8..256c50d5f6b 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -208,6 +208,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *contextmodel.ReqContext) response. SignatureOrg: plugin.SignatureOrg, SecureJsonFields: map[string]bool{}, AngularDetected: plugin.Angular.Detected, + LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), plugin), } if plugin.IsApp() { diff --git a/pkg/api/plugins_test.go b/pkg/api/plugins_test.go index fd1ad0df80f..b517c964f84 100644 --- a/pkg/api/plugins_test.go +++ b/pkg/api/plugins_test.go @@ -41,6 +41,7 @@ import ( "github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" @@ -817,6 +818,7 @@ func Test_PluginsSettings(t *testing.T) { Version: "1.0.0", }, SecureJsonFields: map[string]bool{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, { @@ -841,6 +843,8 @@ func Test_PluginsSettings(t *testing.T) { ErrorCode: tc.errCode, }) } + pluginCDN := pluginscdn.ProvideService(&config.PluginManagementCfg{}) + hs.pluginAssets = pluginassets.ProvideService(hs.Cfg, pluginCDN) hs.pluginErrorResolver = pluginerrs.ProvideStore(errTracker) var err error hs.pluginsUpdateChecker, err = updatechecker.ProvidePluginsService(hs.Cfg, nil, tracing.InitializeTracerForTest()) diff --git a/pkg/plugins/manager/loader/assetpath/assetpath.go b/pkg/plugins/manager/loader/assetpath/assetpath.go index e26ce3bae1d..873e21feb60 100644 --- a/pkg/plugins/manager/loader/assetpath/assetpath.go +++ b/pkg/plugins/manager/loader/assetpath/assetpath.go @@ -57,13 +57,14 @@ func (s *Service) Base(n PluginInfo) (string, error) { return s.cdn.AssetURL(n.pluginJSON.ID, n.pluginJSON.Info.Version, "") } if n.parent != nil { + relPath, err := n.parent.fs.Rel(n.fs.Base()) + if err != nil { + return "", err + } if s.cdn.PluginSupported(n.parent.pluginJSON.ID) { - relPath, err := n.parent.fs.Rel(n.fs.Base()) - if err != nil { - return "", err - } return s.cdn.AssetURL(n.parent.pluginJSON.ID, n.parent.pluginJSON.Info.Version, relPath) } + return path.Join("public/plugins", n.parent.pluginJSON.ID, relPath), nil } return path.Join("public/plugins", n.pluginJSON.ID), nil @@ -87,13 +88,14 @@ func (s *Service) Module(n PluginInfo) (string, error) { return s.cdn.AssetURL(n.pluginJSON.ID, n.pluginJSON.Info.Version, "module.js") } if n.parent != nil { + relPath, err := n.parent.fs.Rel(n.fs.Base()) + if err != nil { + return "", err + } if s.cdn.PluginSupported(n.parent.pluginJSON.ID) { - relPath, err := n.parent.fs.Rel(n.fs.Base()) - if err != nil { - return "", err - } return s.cdn.AssetURL(n.parent.pluginJSON.ID, n.parent.pluginJSON.Info.Version, path.Join(relPath, "module.js")) } + return path.Join("public/plugins", n.parent.pluginJSON.ID, relPath, "module.js"), nil } return path.Join("public/plugins", n.pluginJSON.ID, "module.js"), nil @@ -117,10 +119,6 @@ func (s *Service) RelativeURL(n PluginInfo, pathStr string) (string, error) { return s.cdn.AssetURL(n.parent.pluginJSON.ID, n.parent.pluginJSON.Info.Version, path.Join(relPath, pathStr)) } } - - if s.cdn.PluginSupported(n.pluginJSON.ID) { - return s.cdn.NewCDNURLConstructor(n.pluginJSON.ID, n.pluginJSON.Info.Version).StringPath(pathStr) - } // Local u, err := url.Parse(pathStr) if err != nil { diff --git a/pkg/plugins/manager/loader/assetpath/assetpath_test.go b/pkg/plugins/manager/loader/assetpath/assetpath_test.go index be8ce730b95..04c22b1a478 100644 --- a/pkg/plugins/manager/loader/assetpath/assetpath_test.go +++ b/pkg/plugins/manager/loader/assetpath/assetpath_test.go @@ -187,3 +187,153 @@ func TestService(t *testing.T) { }) } } + +func TestService_ChildPlugins(t *testing.T) { + type expected struct { + module string + baseURL string + relURL string + } + + tcs := []struct { + name string + cfg *config.PluginManagementCfg + pluginInfo func() PluginInfo + expected expected + }{ + { + name: "Local FS external plugin", + cfg: &config.PluginManagementCfg{}, + pluginInfo: func() PluginInfo { + return NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent"), nil) + }, + expected: expected{ + module: "public/plugins/parent/module.js", + baseURL: "public/plugins/parent", + relURL: "public/plugins/parent/path/to/file.txt", + }, + }, + { + name: "Local FS external plugin with child", + cfg: &config.PluginManagementCfg{}, + pluginInfo: func() PluginInfo { + parentInfo := NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent"), nil) + childInfo := NewPluginInfo(plugins.JSONData{ID: "child", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent/child"), &parentInfo) + return childInfo + }, + expected: expected{ + module: "public/plugins/parent/child/module.js", + baseURL: "public/plugins/parent/child", + relURL: "public/plugins/parent/child/path/to/file.txt", + }, + }, + { + name: "Local FS core plugin", + cfg: &config.PluginManagementCfg{}, + pluginInfo: func() PluginInfo { + return NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassCore, plugins.NewLocalFS("/plugins/parent"), nil) + }, + expected: expected{ + module: "core:plugin/parent", + baseURL: "public/app/plugins/parent", + relURL: "public/app/plugins/parent/path/to/file.txt", + }, + }, + { + name: "Externally-built Local FS core plugin", + cfg: &config.PluginManagementCfg{}, + pluginInfo: func() PluginInfo { + return NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassCore, plugins.NewLocalFS("/plugins/parent/dist"), nil) + }, + expected: expected{ + module: "public/plugins/parent/module.js", + baseURL: "public/app/plugins/parent", + relURL: "public/app/plugins/parent/path/to/file.txt", + }, + }, + { + name: "CDN Class plugin", + cfg: &config.PluginManagementCfg{ + PluginsCDNURLTemplate: "https://cdn.example.com", + }, + pluginInfo: func() PluginInfo { + return NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassCDN, pluginFS("https://cdn.example.com/plugins/parent"), nil) + }, + expected: expected{ + module: "https://cdn.example.com/plugins/parent/module.js", + baseURL: "https://cdn.example.com/plugins/parent", + relURL: "https://cdn.example.com/plugins/parent/path/to/file.txt", + }, + }, + { + name: "CDN Class plugin with child", + cfg: &config.PluginManagementCfg{ + PluginsCDNURLTemplate: "https://cdn.example.com", + }, + pluginInfo: func() PluginInfo { + // Note: fake plugin FS is the most convenient way to mock the plugin FS for CDN plugins + parentInfo := NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassCDN, pluginFS("https://cdn.example.com/parent"), nil) + childInfo := NewPluginInfo(plugins.JSONData{ID: "child", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassCDN, pluginFS("https://cdn.example.com/parent/some/other/dir/child"), &parentInfo) + return childInfo + }, + expected: expected{ + module: "https://cdn.example.com/parent/some/other/dir/child/module.js", + baseURL: "https://cdn.example.com/parent/some/other/dir/child", + relURL: "https://cdn.example.com/parent/some/other/dir/child/path/to/file.txt", + }, + }, + { + name: "CDN supported plugin", + cfg: &config.PluginManagementCfg{ + PluginsCDNURLTemplate: "https://cdn.example.com", + PluginSettings: map[string]map[string]string{ + "parent": {"cdn": "true"}, + }, + }, + pluginInfo: func() PluginInfo { + return NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent"), nil) + }, + expected: expected{ + module: "https://cdn.example.com/parent/1.0.0/public/plugins/parent/module.js", + baseURL: "https://cdn.example.com/parent/1.0.0/public/plugins/parent", + relURL: "https://cdn.example.com/parent/1.0.0/public/plugins/parent/path/to/file.txt", + }, + }, + { + name: "CDN supported plugin with child", + cfg: &config.PluginManagementCfg{ + PluginsCDNURLTemplate: "https://cdn.example.com", + PluginSettings: map[string]map[string]string{ + "parent": {"cdn": "true"}, + }, + }, + pluginInfo: func() PluginInfo { + parentInfo := NewPluginInfo(plugins.JSONData{ID: "parent", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent"), nil) + childInfo := NewPluginInfo(plugins.JSONData{ID: "child", Info: plugins.Info{Version: "1.0.0"}}, plugins.ClassExternal, plugins.NewLocalFS("/plugins/parent/child"), &parentInfo) + return childInfo + }, + expected: expected{ + module: "https://cdn.example.com/parent/1.0.0/public/plugins/parent/child/module.js", + baseURL: "https://cdn.example.com/parent/1.0.0/public/plugins/parent/child", + relURL: "https://cdn.example.com/parent/1.0.0/public/plugins/parent/child/path/to/file.txt", + }, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + svc := ProvideService(tc.cfg, pluginscdn.ProvideService(tc.cfg)) + + module, err := svc.Module(tc.pluginInfo()) + require.NoError(t, err) + require.Equal(t, tc.expected.module, module) + + baseURL, err := svc.Base(tc.pluginInfo()) + require.NoError(t, err) + require.Equal(t, tc.expected.baseURL, baseURL) + + relURL, err := svc.RelativeURL(tc.pluginInfo(), "path/to/file.txt") + require.NoError(t, err) + require.Equal(t, tc.expected.relURL, relURL) + }) + } +} diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index f08b192062c..af96b578da4 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -176,6 +176,7 @@ type PluginMetaDTO struct { BaseURL string `json:"baseUrl"` Angular AngularMeta `json:"angular"` MultiValueFilterOperators bool `json:"multiValueFilterOperators"` + LoadingStrategy LoadingStrategy `json:"loadingStrategy"` } type DataSourceDTO struct { @@ -211,28 +212,28 @@ type DataSourceDTO struct { } type PanelDTO struct { - ID string `json:"id"` - Name string `json:"name"` - AliasIDs []string `json:"aliasIds,omitempty"` - Info Info `json:"info"` - HideFromList bool `json:"hideFromList"` - Sort int `json:"sort"` - SkipDataQuery bool `json:"skipDataQuery"` - ReleaseState string `json:"state"` - BaseURL string `json:"baseUrl"` - Signature string `json:"signature"` - Module string `json:"module"` - - Angular AngularMeta `json:"angular"` + ID string `json:"id"` + Name string `json:"name"` + AliasIDs []string `json:"aliasIds,omitempty"` + Info Info `json:"info"` + HideFromList bool `json:"hideFromList"` + Sort int `json:"sort"` + SkipDataQuery bool `json:"skipDataQuery"` + ReleaseState string `json:"state"` + BaseURL string `json:"baseUrl"` + Signature string `json:"signature"` + Module string `json:"module"` + Angular AngularMeta `json:"angular"` + LoadingStrategy LoadingStrategy `json:"loadingStrategy"` } type AppDTO struct { - ID string `json:"id"` - Path string `json:"path"` - Version string `json:"version"` - Preload bool `json:"preload"` - - Angular AngularMeta `json:"angular"` + ID string `json:"id"` + Path string `json:"path"` + Version string `json:"version"` + Preload bool `json:"preload"` + Angular AngularMeta `json:"angular"` + LoadingStrategy LoadingStrategy `json:"loadingStrategy"` } const ( @@ -252,6 +253,13 @@ type Error struct { message string `json:"-"` } +type LoadingStrategy string + +const ( + LoadingStrategyFetch LoadingStrategy = "fetch" + LoadingStrategyScript LoadingStrategy = "script" +) + func (e Error) Error() string { if e.message != "" { return e.message diff --git a/pkg/services/pluginsintegration/loader/loader_test.go b/pkg/services/pluginsintegration/loader/loader_test.go index 411825ff306..2fed29e25a4 100644 --- a/pkg/services/pluginsintegration/loader/loader_test.go +++ b/pkg/services/pluginsintegration/loader/loader_test.go @@ -1243,8 +1243,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Plugins: []plugins.Dependency{}, }, }, - Module: "public/plugins/test-panel/module.js", - BaseURL: "public/plugins/test-panel", + Module: "public/plugins/test-datasource/nested/module.js", + BaseURL: "public/plugins/test-datasource/nested", FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "nested-plugins/parent/nested")), Signature: plugins.SignatureStatusValid, SignatureType: plugins.SignatureTypeGrafana, @@ -1408,8 +1408,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { {Name: "License", URL: "https://github.com/grafana/grafana-starter-panel/blob/master/LICENSE"}, }, Logos: plugins.Logos{ - Small: "public/plugins/myorgid-simple-panel/img/logo.svg", - Large: "public/plugins/myorgid-simple-panel/img/logo.svg", + Small: "public/plugins/myorgid-simple-app/child/img/logo.svg", + Large: "public/plugins/myorgid-simple-app/child/img/logo.svg", }, Screenshots: []plugins.Screenshots{}, Description: "Grafana Panel Plugin Template", @@ -1423,8 +1423,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Plugins: []plugins.Dependency{}, }, }, - Module: "public/plugins/myorgid-simple-panel/module.js", - BaseURL: "public/plugins/myorgid-simple-panel", + Module: "public/plugins/myorgid-simple-app/child/module.js", + BaseURL: "public/plugins/myorgid-simple-app/child", FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "app-with-child/dist/child")), IncludedInAppID: parent.ID, Signature: plugins.SignatureStatusValid, diff --git a/pkg/services/pluginsintegration/pluginassets/pluginassets.go b/pkg/services/pluginsintegration/pluginassets/pluginassets.go new file mode 100644 index 00000000000..a92aabecb6f --- /dev/null +++ b/pkg/services/pluginsintegration/pluginassets/pluginassets.go @@ -0,0 +1,81 @@ +package pluginassets + +import ( + "context" + + "github.com/Masterminds/semver/v3" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/pluginscdn" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/grafana/grafana/pkg/setting" +) + +const ( + CreatePluginVersionCfgKey = "create_plugin_version" + CreatePluginVersionScriptSupportEnabled = "4.15.0" +) + +var ( + scriptLoadingMinSupportedVersion = semver.MustParse(CreatePluginVersionScriptSupportEnabled) +) + +func ProvideService(cfg *setting.Cfg, cdn *pluginscdn.Service) *Service { + return &Service{ + cfg: cfg, + cdn: cdn, + log: log.New("pluginassets"), + } +} + +type Service struct { + cfg *setting.Cfg + cdn *pluginscdn.Service + log log.Logger +} + +// LoadingStrategy calculates the loading strategy for a plugin. +// If a plugin has plugin setting `create_plugin_version` >= 4.15.0, set loadingStrategy to "script". +// If a plugin is not loaded via the CDN and is not Angular, set loadingStrategy to "script". +// Otherwise, set loadingStrategy to "fetch". +func (s *Service) LoadingStrategy(_ context.Context, p pluginstore.Plugin) plugins.LoadingStrategy { + if pCfg, ok := s.cfg.PluginSettings[p.ID]; ok { + if s.compatibleCreatePluginVersion(pCfg) { + return plugins.LoadingStrategyScript + } + } + + // If the plugin has a parent, check the parent's create_plugin_version setting + if p.Parent != nil { + if pCfg, ok := s.cfg.PluginSettings[p.Parent.ID]; ok { + if s.compatibleCreatePluginVersion(pCfg) { + return plugins.LoadingStrategyScript + } + } + } + + if !s.cndEnabled(p) && !p.Angular.Detected { + return plugins.LoadingStrategyScript + } + + return plugins.LoadingStrategyFetch +} + +func (s *Service) compatibleCreatePluginVersion(ps map[string]string) bool { + if cpv, ok := ps[CreatePluginVersionCfgKey]; ok { + createPluginVer, err := semver.NewVersion(cpv) + if err != nil { + s.log.Warn("Failed to parse create plugin version setting as semver", "version", cpv, "error", err) + } else { + if !createPluginVer.LessThan(scriptLoadingMinSupportedVersion) { + return true + } + } + } + return false +} + +func (s *Service) cndEnabled(p pluginstore.Plugin) bool { + return s.cdn.PluginSupported(p.ID) || p.Class == plugins.ClassCDN +} diff --git a/pkg/services/pluginsintegration/pluginassets/pluginassets_test.go b/pkg/services/pluginsintegration/pluginassets/pluginassets_test.go new file mode 100644 index 00000000000..3ca9c4bbccb --- /dev/null +++ b/pkg/services/pluginsintegration/pluginassets/pluginassets_test.go @@ -0,0 +1,168 @@ +package pluginassets + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/config" + "github.com/grafana/grafana/pkg/plugins/pluginscdn" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/grafana/grafana/pkg/setting" +) + +func TestService_Calculate(t *testing.T) { + const pluginID = "grafana-test-datasource" + + const ( + incompatVersion = "4.14.0" + compatVersion = CreatePluginVersionScriptSupportEnabled + futureVersion = "5.0.0" + ) + + tcs := []struct { + name string + pluginSettings setting.PluginSettings + plugin pluginstore.Plugin + expected plugins.LoadingStrategy + }{ + { + name: "Expected LoadingStrategyScript when create-plugin version is compatible and plugin is not angular", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: compatVersion, + }), + plugin: newPlugin(pluginID, false), + expected: plugins.LoadingStrategyScript, + }, + { + name: "Expected LoadingStrategyScript when parent create-plugin version is compatible and plugin is not angular", + pluginSettings: newPluginSettings("parent-datasource", map[string]string{ + CreatePluginVersionCfgKey: compatVersion, + }), + plugin: newPlugin(pluginID, false, func(p pluginstore.Plugin) pluginstore.Plugin { + p.Parent = &pluginstore.ParentPlugin{ID: "parent-datasource"} + return p + }), + expected: plugins.LoadingStrategyScript, + }, + { + name: "Expected LoadingStrategyScript when create-plugin version is future compatible and plugin is not angular", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: futureVersion, + }), + plugin: newPlugin(pluginID, false), + expected: plugins.LoadingStrategyScript, + }, + { + name: "Expected LoadingStrategyScript when create-plugin version is not provided, plugin is not angular and is not configured as CDN enabled", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + // NOTE: cdn key is not set + }), + plugin: newPlugin(pluginID, false), + expected: plugins.LoadingStrategyScript, + }, + { + name: "Expected LoadingStrategyScript when create-plugin version is not compatible, plugin is not angular, is not configured as CDN enabled and does not have the CDN class", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: incompatVersion, + // NOTE: cdn key is not set + }), + plugin: newPlugin(pluginID, false, func(p pluginstore.Plugin) pluginstore.Plugin { + p.Class = plugins.ClassExternal + return p + }), + expected: plugins.LoadingStrategyScript, + }, + { + name: "Expected LoadingStrategyFetch when create-plugin version is not compatible, plugin is not angular, is configured as CDN enabled and does not have the CDN class", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + "cdn": "true", + CreatePluginVersionCfgKey: incompatVersion, + }), + plugin: newPlugin(pluginID, false, func(p pluginstore.Plugin) pluginstore.Plugin { + p.Class = plugins.ClassExternal + return p + }), + expected: plugins.LoadingStrategyFetch, + }, + { + name: "Expected LoadingStrategyFetch when create-plugin version is not compatible and plugin is angular", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: incompatVersion, + }), + plugin: newPlugin(pluginID, true), + expected: plugins.LoadingStrategyFetch, + }, + { + name: "Expected LoadingStrategyFetch when create-plugin version is not compatible, plugin is not angular and plugin is configured as CDN enabled", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + "cdn": "true", + CreatePluginVersionCfgKey: incompatVersion, + }), + plugin: newPlugin(pluginID, false), + expected: plugins.LoadingStrategyFetch, + }, + { + name: "Expected LoadingStrategyFetch when create-plugin version is not compatible, plugin is not angular and has the CDN class", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: incompatVersion, + }), + plugin: newPlugin(pluginID, false, func(p pluginstore.Plugin) pluginstore.Plugin { + p.Class = plugins.ClassCDN + return p + }), + expected: plugins.LoadingStrategyFetch, + }, + { + name: "Expected LoadingStrategyScript when plugin setting create-plugin version is badly formatted, plugin is not configured as CDN enabled and does not have the CDN class", + pluginSettings: newPluginSettings(pluginID, map[string]string{ + CreatePluginVersionCfgKey: "invalidSemver", + }), + plugin: newPlugin(pluginID, false), + expected: plugins.LoadingStrategyScript, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + s := &Service{ + cfg: newCfg(tc.pluginSettings), + cdn: pluginscdn.ProvideService(&config.PluginManagementCfg{ + PluginsCDNURLTemplate: "http://cdn.example.com", // required for cdn.PluginSupported check + PluginSettings: tc.pluginSettings, + }), + log: log.NewNopLogger(), + } + + got := s.LoadingStrategy(context.Background(), tc.plugin) + assert.Equal(t, tc.expected, got, "unexpected loading strategy") + }) + } +} + +func newPlugin(pluginID string, angular bool, cbs ...func(p pluginstore.Plugin) pluginstore.Plugin) pluginstore.Plugin { + p := pluginstore.Plugin{ + JSONData: plugins.JSONData{ + ID: pluginID, + }, + Angular: plugins.AngularMeta{Detected: angular}, + } + for _, cb := range cbs { + p = cb(p) + } + return p +} + +func newCfg(ps setting.PluginSettings) *setting.Cfg { + return &setting.Cfg{ + PluginSettings: ps, + } +} + +func newPluginSettings(pluginID string, kv map[string]string) setting.PluginSettings { + return setting.PluginSettings{ + pluginID: kv, + } +} diff --git a/pkg/services/pluginsintegration/pluginsintegration.go b/pkg/services/pluginsintegration/pluginsintegration.go index cc1ec4c80cf..a699982efd8 100644 --- a/pkg/services/pluginsintegration/pluginsintegration.go +++ b/pkg/services/pluginsintegration/pluginsintegration.go @@ -43,6 +43,7 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/loader" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" @@ -126,6 +127,7 @@ var WireSet = wire.NewSet( plugincontext.ProvideBaseService, wire.Bind(new(plugincontext.BasePluginContextProvider), new(*plugincontext.BaseProvider)), plugininstaller.ProvideService, + pluginassets.ProvideService, ) // WireExtensionSet provides a wire.ProviderSet of plugin providers that can be diff --git a/pkg/services/pluginsintegration/pluginstore/plugins.go b/pkg/services/pluginsintegration/pluginstore/plugins.go index 194ba92ade4..f041c77c5e5 100644 --- a/pkg/services/pluginsintegration/pluginstore/plugins.go +++ b/pkg/services/pluginsintegration/pluginstore/plugins.go @@ -16,6 +16,7 @@ type Plugin struct { Class plugins.Class // App fields + Parent *ParentPlugin IncludedInAppID string DefaultNavURL string Pinned bool @@ -59,7 +60,7 @@ func ToGrafanaDTO(p *plugins.Plugin) Plugin { supportsStreaming = true } - return Plugin{ + dto := Plugin{ fs: p.FS, supportsStreaming: supportsStreaming, Class: p.Class, @@ -76,4 +77,14 @@ func ToGrafanaDTO(p *plugins.Plugin) Plugin { ExternalService: p.ExternalService, Angular: p.Angular, } + + if p.Parent != nil { + dto.Parent = &ParentPlugin{ID: p.Parent.ID} + } + + return dto +} + +type ParentPlugin struct { + ID string } diff --git a/public/app/features/alerting/unified/mocks/server/handlers/plugins.ts b/public/app/features/alerting/unified/mocks/server/handlers/plugins.ts index 93f28c903b2..60f641dc089 100644 --- a/public/app/features/alerting/unified/mocks/server/handlers/plugins.ts +++ b/public/app/features/alerting/unified/mocks/server/handlers/plugins.ts @@ -1,6 +1,6 @@ import { http, HttpResponse } from 'msw'; -import { PluginMeta } from '@grafana/data'; +import { PluginLoadingStrategy, PluginMeta } from '@grafana/data'; import { config } from '@grafana/runtime'; import { plugins } from 'app/features/alerting/unified/testSetup/plugins'; @@ -18,6 +18,7 @@ export const getPluginsHandler = (pluginsArray: PluginMeta[] = plugins) => { preload: true, version: info.version, angular: angular ?? { detected: false, hideDeprecation: false }, + loadingStrategy: PluginLoadingStrategy.script, }; }); diff --git a/public/app/features/alerting/unified/utils/rules.test.ts b/public/app/features/alerting/unified/utils/rules.test.ts index e6ce13414cc..59d77e3b3d3 100644 --- a/public/app/features/alerting/unified/utils/rules.test.ts +++ b/public/app/features/alerting/unified/utils/rules.test.ts @@ -1,3 +1,4 @@ +import { PluginLoadingStrategy } from '@grafana/data'; import { config } from '@grafana/runtime'; import { RuleGroupIdentifier } from 'app/types/unified-alerting'; @@ -47,6 +48,7 @@ describe('getRuleOrigin', () => { path: '', preload: true, angular: { detected: false, hideDeprecation: false }, + loadingStrategy: PluginLoadingStrategy.script, }, }; const rule = mockCombinedRule({ diff --git a/public/app/features/plugins/importPanelPlugin.ts b/public/app/features/plugins/importPanelPlugin.ts index 62dbcb8c4be..81819a8745f 100644 --- a/public/app/features/plugins/importPanelPlugin.ts +++ b/public/app/features/plugins/importPanelPlugin.ts @@ -1,6 +1,6 @@ import { ComponentType } from 'react'; -import { PanelPlugin, PanelPluginMeta, PanelProps } from '@grafana/data'; +import { PanelPlugin, PanelPluginMeta, PanelProps, PluginLoadingStrategy } from '@grafana/data'; import config from 'app/core/config'; import { getPanelPluginLoadError } from '../panel/components/PanelPluginError'; @@ -56,10 +56,12 @@ export function syncGetPanelPlugin(id: string): PanelPlugin | undefined { } function getPanelPlugin(meta: PanelPluginMeta): Promise { + const fallbackLoadingStrategy = meta.loadingStrategy ?? PluginLoadingStrategy.fetch; return importPluginModule({ path: meta.module, version: meta.info?.version, isAngular: meta.angular?.detected, + loadingStrategy: fallbackLoadingStrategy, pluginId: meta.id, }) .then((pluginExports) => { diff --git a/public/app/features/plugins/loader/cache.test.ts b/public/app/features/plugins/loader/cache.test.ts index 531cadbe583..39fa44576be 100644 --- a/public/app/features/plugins/loader/cache.test.ts +++ b/public/app/features/plugins/loader/cache.test.ts @@ -1,4 +1,12 @@ -import { registerPluginInCache, invalidatePluginInCache, resolveWithCache, getPluginFromCache } from './cache'; +import { PluginLoadingStrategy } from '@grafana/data'; + +import { + registerPluginInCache, + invalidatePluginInCache, + resolveWithCache, + getPluginFromCache, + extractCacheKeyFromPath, +} from './cache'; jest.mock('./constants', () => ({ CACHE_INITIALISED_AT: 123456, @@ -7,28 +15,28 @@ jest.mock('./constants', () => ({ describe('Cache Functions', () => { describe('registerPluginInCache', () => { it('should register a plugin in the cache', () => { - const plugin = { pluginId: 'plugin1', version: '1.0.0', isAngular: false }; - registerPluginInCache(plugin); + const plugin = { version: '1.0.0', loadingStrategy: PluginLoadingStrategy.script }; + registerPluginInCache({ path: 'public/plugins/plugin1/module.js', ...plugin }); expect(getPluginFromCache('plugin1')).toEqual(plugin); }); it('should not register a plugin if it already exists in the cache', () => { - const pluginId = 'plugin2'; - const plugin = { pluginId, version: '2.0.0' }; + const path = 'public/plugins/plugin2/module.js'; + const plugin = { path, version: '2.0.0', loadingStrategy: PluginLoadingStrategy.script }; registerPluginInCache(plugin); - const plugin2 = { pluginId, version: '2.5.0' }; + const plugin2 = { path, version: '2.5.0', loadingStrategy: PluginLoadingStrategy.script }; registerPluginInCache(plugin2); - expect(getPluginFromCache(pluginId)?.version).toBe('2.0.0'); + expect(getPluginFromCache(path)?.version).toBe('2.0.0'); }); }); describe('invalidatePluginInCache', () => { it('should invalidate a plugin in the cache', () => { - const pluginId = 'plugin3'; - const plugin = { pluginId, version: '3.0.0' }; + const path = 'public/plugins/plugin2/module.js'; + const plugin = { path, version: '3.0.0', loadingStrategy: PluginLoadingStrategy.script }; registerPluginInCache(plugin); - invalidatePluginInCache(pluginId); - expect(getPluginFromCache(pluginId)).toBeUndefined(); + invalidatePluginInCache('plugin2'); + expect(getPluginFromCache('plugin2')).toBeUndefined(); }); it('should not throw an error if the plugin does not exist in the cache', () => { @@ -43,17 +51,43 @@ describe('Cache Functions', () => { }); it('should resolve URL with plugin version as cache bust parameter if available', () => { - const plugin = { pluginId: 'plugin5', version: '5.0.0' }; - registerPluginInCache(plugin); const url = 'http://localhost:3000/public/plugins/plugin5/module.js'; + const plugin = { path: url, version: '5.0.0', loadingStrategy: PluginLoadingStrategy.script }; + registerPluginInCache(plugin); expect(resolveWithCache(url)).toContain('_cache=5.0.0'); }); }); + describe('extractCacheKeyFromPath', () => { + it('should extract plugin ID from a path', () => { + expect(extractCacheKeyFromPath('public/plugins/plugin6/module.js')).toBe('plugin6'); + }); + + it('should extract plugin ID from a path', () => { + expect(extractCacheKeyFromPath('public/plugins/plugin6/datasource/module.js')).toBe('plugin6'); + }); + + it('should extract plugin ID from a url', () => { + expect(extractCacheKeyFromPath('https://my-url.com/plugin6/1.0.0/public/plugins/plugin6/module.js')).toBe( + 'plugin6' + ); + }); + + it('should extract plugin ID from a nested plugin url', () => { + expect( + extractCacheKeyFromPath('https://my-url.com/plugin6/1.0.0/public/plugins/plugin6/datasource/module.js') + ).toBe('plugin6'); + }); + + it('should return null if the path does not match the pattern', () => { + expect(extractCacheKeyFromPath('public/plugins/plugin7')).toBeNull(); + }); + }); + describe('getPluginFromCache', () => { it('should return plugin from cache if exists', () => { - const plugin = { pluginId: 'plugin6', version: '6.0.0' }; - registerPluginInCache(plugin); + const plugin = { version: '6.0.0', loadingStrategy: PluginLoadingStrategy.script }; + registerPluginInCache({ path: 'public/plugins/plugin6/module.js', ...plugin }); expect(getPluginFromCache('plugin6')).toEqual(plugin); }); diff --git a/public/app/features/plugins/loader/cache.ts b/public/app/features/plugins/loader/cache.ts index 4b3fa40d031..00e77489efd 100644 --- a/public/app/features/plugins/loader/cache.ts +++ b/public/app/features/plugins/loader/cache.ts @@ -1,22 +1,26 @@ +import { PluginLoadingStrategy } from '@grafana/data'; + import { clearPluginSettingsCache } from '../pluginSettings'; import { CACHE_INITIALISED_AT } from './constants'; -const cache: Record = {}; +const cache: Record = {}; type CacheablePlugin = { - pluginId: string; + path: string; version: string; - isAngular?: boolean; + loadingStrategy: PluginLoadingStrategy; }; -export function registerPluginInCache({ pluginId, version, isAngular }: CacheablePlugin): void { - const key = pluginId; +type CachedPlugin = Omit; + +export function registerPluginInCache({ path, version, loadingStrategy }: CacheablePlugin): void { + const key = extractCacheKeyFromPath(path); + if (key && !cache[key]) { cache[key] = { version: encodeURI(version), - isAngular, - pluginId, + loadingStrategy, }; } } @@ -39,7 +43,7 @@ export function resolveWithCache(url: string, defaultBust = CACHE_INITIALISED_AT return `${url}?_cache=${bust}`; } -export function getPluginFromCache(path: string): CacheablePlugin | undefined { +export function getPluginFromCache(path: string): CachedPlugin | undefined { const key = getCacheKey(path); if (!key) { return; @@ -47,6 +51,12 @@ export function getPluginFromCache(path: string): CacheablePlugin | undefined { return cache[key]; } +export function extractCacheKeyFromPath(path: string) { + const regex = /\/?public\/plugins\/([^\/]+)\//; + const match = path.match(regex); + return match ? match[1] : null; +} + function getCacheKey(address: string): string | undefined { const key = Object.keys(cache).find((key) => address.includes(key)); if (!key) { diff --git a/public/app/features/plugins/pluginPreloader.ts b/public/app/features/plugins/pluginPreloader.ts index 024d743a652..378ad0ba257 100644 --- a/public/app/features/plugins/pluginPreloader.ts +++ b/public/app/features/plugins/pluginPreloader.ts @@ -5,7 +5,7 @@ import { startMeasure, stopMeasure } from 'app/core/utils/metrics'; import { getPluginSettings } from 'app/features/plugins/pluginSettings'; import { PluginExtensionRegistries } from './extensions/registry/types'; -import * as pluginLoader from './plugin_loader'; +import { importPluginModule } from './plugin_loader'; export type PluginPreloadResult = { pluginId: string; @@ -48,14 +48,15 @@ export async function preloadPlugins( } async function preload(config: AppPluginConfig): Promise { - const { path, version, id: pluginId } = config; + const { path, version, id: pluginId, loadingStrategy } = config; try { startMeasure(`frontend_plugin_preload_${pluginId}`); - const { plugin } = await pluginLoader.importPluginModule({ + const { plugin } = await importPluginModule({ path, version, isAngular: config.angular.detected, pluginId, + loadingStrategy, }); const { exposedComponentConfigs = [], addedComponentConfigs = [], addedLinkConfigs = [] } = plugin; diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index 588e938484b..e9f7866761b 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -4,6 +4,7 @@ import { DataSourceJsonData, DataSourcePlugin, DataSourcePluginMeta, + PluginLoadingStrategy, PluginMeta, } from '@grafana/data'; import { DataQuery } from '@grafana/schema'; @@ -18,7 +19,7 @@ import { SystemJS } from './loader/systemjs'; import { sharedDependenciesMap } from './loader/sharedDependencies'; import { decorateSystemJSFetch, decorateSystemJSResolve, decorateSystemJsOnload } from './loader/systemjsHooks'; import { SystemJSWithLoaderHooks } from './loader/types'; -import { buildImportMap, isHostedOnCDN, resolveModulePath } from './loader/utils'; +import { buildImportMap, resolveModulePath } from './loader/utils'; import { importPluginModuleInSandbox } from './sandbox/sandbox_plugin_loader'; import { isFrontendSandboxSupported } from './sandbox/utils'; @@ -29,13 +30,17 @@ SystemJS.addImportMap({ imports }); const systemJSPrototype: SystemJSWithLoaderHooks = SystemJS.constructor.prototype; // This instructs SystemJS to load plugin assets using fetch and eval if it returns a truthy value, otherwise -// it will load the plugin using a script tag. We only want to fetch and eval files that are -// hosted on a CDN, are related to Angular plugins or are not js files. +// it will load the plugin using a script tag. The logic that sets loadingStrategy comes from the backend. +// See: pkg/services/pluginsintegration/pluginassets/pluginassets.go systemJSPrototype.shouldFetch = function (url) { const pluginInfo = getPluginFromCache(url); const jsTypeRegEx = /^[^#?]+\.(js)([?#].*)?$/; - return isHostedOnCDN(url) || Boolean(pluginInfo?.isAngular) || !jsTypeRegEx.test(url); + if (!jsTypeRegEx.test(url)) { + return true; + } + + return Boolean(pluginInfo?.loadingStrategy !== PluginLoadingStrategy.script); }; const originalImport = systemJSPrototype.import; @@ -64,17 +69,19 @@ systemJSPrototype.onload = decorateSystemJsOnload; export async function importPluginModule({ path, + pluginId, + loadingStrategy, version, isAngular, - pluginId, }: { path: string; pluginId: string; + loadingStrategy: PluginLoadingStrategy; version?: string; isAngular?: boolean; }): Promise { if (version) { - registerPluginInCache({ pluginId, version, isAngular }); + registerPluginInCache({ path, version, loadingStrategy }); } const builtIn = builtInPlugins[path]; @@ -99,10 +106,12 @@ export async function importPluginModule({ export function importDataSourcePlugin(meta: DataSourcePluginMeta): Promise { const isAngular = meta.angular?.detected ?? meta.angularDetected; + const fallbackLoadingStrategy = meta.loadingStrategy ?? PluginLoadingStrategy.fetch; return importPluginModule({ path: meta.module, version: meta.info?.version, isAngular, + loadingStrategy: fallbackLoadingStrategy, pluginId: meta.id, }).then((pluginExports) => { if (pluginExports.plugin) { @@ -128,10 +137,12 @@ export function importDataSourcePlugin(meta: DataSourcePluginMeta): Promise { const isAngular = meta.angular?.detected ?? meta.angularDetected; + const fallbackLoadingStrategy = meta.loadingStrategy ?? PluginLoadingStrategy.fetch; return importPluginModule({ path: meta.module, version: meta.info?.version, isAngular, + loadingStrategy: fallbackLoadingStrategy, pluginId: meta.id, }).then((pluginExports) => { const plugin: AppPlugin = pluginExports.plugin ? pluginExports.plugin : new AppPlugin(); From adf1402ff8409bf8a0dbebade0a943d5ea37d528 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Mon, 9 Sep 2024 13:54:43 +0300 Subject: [PATCH 03/18] Grafana/ui: Update Link component to react router 6 (#92976) * Grafana/ui: Update Link component to react router 6 * Add compat package to grafana/ui --- package.json | 2 +- packages/grafana-ui/package.json | 3 +- .../grafana-ui/src/components/Link/Link.tsx | 2 +- .../src/components/Link/TextLink.test.tsx | 2 +- .../plugins/components/AppRootPage.test.tsx | 14 ++------ yarn.lock | 34 +++++-------------- 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 4f8297e540b..757797e0993 100644 --- a/package.json +++ b/package.json @@ -375,7 +375,7 @@ "react-responsive-carousel": "^3.2.23", "react-router": "5.3.3", "react-router-dom": "5.3.3", - "react-router-dom-v5-compat": "^6.10.0", + "react-router-dom-v5-compat": "^6.26.1", "react-select": "5.8.0", "react-split-pane": "0.1.92", "react-table": "7.8.0", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 58c2acd67c0..c127aa3f076 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -98,7 +98,7 @@ "react-i18next": "^14.0.0", "react-inlinesvg": "3.0.2", "react-loading-skeleton": "3.4.0", - "react-router-dom": "5.3.3", + "react-router-dom-v5-compat": "^6.26.1", "react-select": "5.8.0", "react-table": "7.8.0", "react-transition-group": "4.4.5", @@ -151,7 +151,6 @@ "@types/react-color": "3.0.12", "@types/react-dom": "18.2.25", "@types/react-highlight-words": "0.20.0", - "@types/react-router-dom": "5.3.3", "@types/react-test-renderer": "18.3.0", "@types/react-transition-group": "4.4.11", "@types/react-window": "1.8.8", diff --git a/packages/grafana-ui/src/components/Link/Link.tsx b/packages/grafana-ui/src/components/Link/Link.tsx index 390a681265c..8194fbb98b1 100644 --- a/packages/grafana-ui/src/components/Link/Link.tsx +++ b/packages/grafana-ui/src/components/Link/Link.tsx @@ -1,5 +1,5 @@ import { AnchorHTMLAttributes, forwardRef } from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router-dom-v5-compat'; import { locationUtil, textUtil } from '@grafana/data'; diff --git a/packages/grafana-ui/src/components/Link/TextLink.test.tsx b/packages/grafana-ui/src/components/Link/TextLink.test.tsx index f1cb7d77468..d31917b4d7d 100644 --- a/packages/grafana-ui/src/components/Link/TextLink.test.tsx +++ b/packages/grafana-ui/src/components/Link/TextLink.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; +import { MemoryRouter } from 'react-router-dom-v5-compat'; import { GrafanaConfig, locationUtil } from '@grafana/data'; diff --git a/public/app/features/plugins/components/AppRootPage.test.tsx b/public/app/features/plugins/components/AppRootPage.test.tsx index 8afc60c079b..db244781749 100644 --- a/public/app/features/plugins/components/AppRootPage.test.tsx +++ b/public/app/features/plugins/components/AppRootPage.test.tsx @@ -1,18 +1,15 @@ -import { act, render, screen } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import { Component } from 'react'; -import { Provider } from 'react-redux'; import { Route, Router } from 'react-router-dom'; -import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { render } from 'test/test-utils'; import { AppPlugin, PluginType, AppRootProps, NavModelItem, PluginIncludeType, OrgRole } from '@grafana/data'; import { getMockPlugin } from '@grafana/data/test/__mocks__/pluginMocks'; import { locationService, setEchoSrv } from '@grafana/runtime'; -import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { GrafanaRoute } from 'app/core/navigation/GrafanaRoute'; import { RouteDescriptor } from 'app/core/navigation/types'; import { contextSrv } from 'app/core/services/context_srv'; import { Echo } from 'app/core/services/echo/Echo'; -import { configureStore } from 'app/store/configureStore'; import { getPluginSettings } from '../pluginSettings'; import { importAppPlugin } from '../plugin_loader'; @@ -89,7 +86,6 @@ async function renderUnderRouter(page = '') { appPluginNavItem.parentItem = appsSection; const pagePath = page ? `/${page}` : ''; - const store = configureStore(); const route = { component: () => , } as unknown as RouteDescriptor; @@ -100,11 +96,7 @@ async function renderUnderRouter(page = '') { render( - - - } /> - - + } /> ); } diff --git a/yarn.lock b/yarn.lock index 1d2bb386279..2924d598da4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4092,7 +4092,6 @@ __metadata: "@types/react-color": "npm:3.0.12" "@types/react-dom": "npm:18.2.25" "@types/react-highlight-words": "npm:0.20.0" - "@types/react-router-dom": "npm:5.3.3" "@types/react-table": "npm:7.7.20" "@types/react-test-renderer": "npm:18.3.0" "@types/react-transition-group": "npm:4.4.11" @@ -4146,7 +4145,7 @@ __metadata: react-i18next: "npm:^14.0.0" react-inlinesvg: "npm:3.0.2" react-loading-skeleton: "npm:3.4.0" - react-router-dom: "npm:5.3.3" + react-router-dom-v5-compat: "npm:^6.26.1" react-select: "npm:5.8.0" react-select-event: "npm:^5.1.0" react-table: "npm:7.8.0" @@ -6725,13 +6724,6 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.14.2": - version: 1.14.2 - resolution: "@remix-run/router@npm:1.14.2" - checksum: 10/422844e88b985f1e287301b302c6cf8169c9eea792f80d40464f97b25393bb2e697228ebd7a7b61444d5a51c5873c4a637aad20acde5886a5caf62e833c5ceee - languageName: node - linkType: hard - "@remix-run/router@npm:1.19.1": version: 1.19.1 resolution: "@remix-run/router@npm:1.19.1" @@ -18797,7 +18789,7 @@ __metadata: react-responsive-carousel: "npm:^3.2.23" react-router: "npm:5.3.3" react-router-dom: "npm:5.3.3" - react-router-dom-v5-compat: "npm:^6.10.0" + react-router-dom-v5-compat: "npm:^6.26.1" react-select: "npm:5.8.0" react-select-event: "npm:5.5.1" react-split-pane: "npm:0.1.92" @@ -27460,17 +27452,18 @@ __metadata: languageName: node linkType: hard -"react-router-dom-v5-compat@npm:^6.10.0": - version: 6.21.3 - resolution: "react-router-dom-v5-compat@npm:6.21.3" +"react-router-dom-v5-compat@npm:^6.26.1": + version: 6.26.1 + resolution: "react-router-dom-v5-compat@npm:6.26.1" dependencies: + "@remix-run/router": "npm:1.19.1" history: "npm:^5.3.0" - react-router: "npm:6.21.3" + react-router: "npm:6.26.1" peerDependencies: react: ">=16.8" react-dom: ">=16.8" react-router-dom: 4 || 5 - checksum: 10/ed40e3e241a8c2a85aa0a95b52b77b9b9667e3f1a48a8a60e0b33e3c615c3b490b6c3a645ea92efc5783251111d039a5c809b779e1a93b9da23de6e97e4e1bd0 + checksum: 10/c8249404e0b219ff00797ae0ff8818394a07309b62617a9a4e47d5a8769801098431ceec38eca8c712cbee4a04843326cb2334ed15ea08b7848b5ce5a58ad0a3 languageName: node linkType: hard @@ -27524,17 +27517,6 @@ __metadata: languageName: node linkType: hard -"react-router@npm:6.21.3": - version: 6.21.3 - resolution: "react-router@npm:6.21.3" - dependencies: - "@remix-run/router": "npm:1.14.2" - peerDependencies: - react: ">=16.8" - checksum: 10/3d5107cfdb440519d84e6ad6d95454e3bf41ec97677b95f7b2a7f281f8ddf191b765cf1b599ead951f3cd33ed4429f140590d74a01cfdf835dc2f812023a978a - languageName: node - linkType: hard - "react-router@npm:6.26.1": version: 6.26.1 resolution: "react-router@npm:6.26.1" From 5ecc927f084e63091eec9dde72c4d0e6a3fdca06 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Mon, 9 Sep 2024 13:10:36 +0200 Subject: [PATCH 04/18] Explore Metrics: Skip DataTrailsHistory tests (#93096) * Fix tests * Skip tests * revert the change --- public/app/features/trails/DataTrailsHistory.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/app/features/trails/DataTrailsHistory.test.tsx b/public/app/features/trails/DataTrailsHistory.test.tsx index 85ed9128279..13e65c6adce 100644 --- a/public/app/features/trails/DataTrailsHistory.test.tsx +++ b/public/app/features/trails/DataTrailsHistory.test.tsx @@ -16,7 +16,9 @@ type ParseFilterTestCase = { }; describe('DataTrailsHistory', () => { - describe('parseTimeTooltip', () => { + // Due to daylight saving changes the expected time differs depends on when we run the tests. + // Until we find a better way to test, those will be skipped. + describe.skip('parseTimeTooltip', () => { // global timezone is set to Pacific/Easter, see jest-config.js file test.each([ { From 7afe6d7023435afdde0a12198192e27461146228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Farkas?= Date: Mon, 9 Sep 2024 13:31:05 +0200 Subject: [PATCH 05/18] CODEOWNERS: improve ownership of query apis (#93087) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cfc637c729b..91e8e796005 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -76,6 +76,7 @@ /pkg/api/ @grafana/grafana-backend-group /pkg/apis/ @grafana/grafana-app-platform-squad /pkg/apis/alerting_notifications @grafana/grafana-app-platform-squad @grafana/alerting-backend @grafana/alerting-frontend +/pkg/apis/query @grafana/grafana-datasources-core-services /pkg/bus/ @grafana/grafana-search-and-storage /pkg/cmd/ @grafana/grafana-backend-group /pkg/cmd/grafana/apiserver @grafana/grafana-app-platform-squad @@ -662,6 +663,7 @@ embed.go @grafana/grafana-as-code /pkg/registry/ @grafana/grafana-as-code /pkg/registry/apis/ @grafana/grafana-app-platform-squad /pkg/registry/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend +/pkg/registry/apis/query @grafana/grafana-datasources-core-services /pkg/codegen/ @grafana/grafana-as-code /pkg/codegen/generators @grafana/grafana-as-code /pkg/kinds/*/*_gen.go @grafana/grafana-as-code From 6548ea377d3bdea29ce8d7e4fe44993694a258ac Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Mon, 9 Sep 2024 13:36:59 +0200 Subject: [PATCH 06/18] Chore: Bump grafana-plugin-sdk-go version to v0.247.0 (#93095) * Bump grafana-plugin-sdk-go version to v0.247.0 * make update-workspace * make update-workspace --- go.mod | 18 ++++++------- go.sum | 39 +++++++++++++++-------------- go.work.sum | 14 +++++++++++ pkg/aggregator/go.mod | 16 ++++++------ pkg/aggregator/go.sum | 32 +++++++++++------------ pkg/apimachinery/go.mod | 10 ++++---- pkg/apimachinery/go.sum | 20 +++++++-------- pkg/apiserver/go.mod | 12 ++++----- pkg/apiserver/go.sum | 28 ++++++++++----------- pkg/build/go.mod | 10 ++++---- pkg/build/go.sum | 20 +++++++-------- pkg/promlib/go.mod | 12 ++++----- pkg/promlib/go.sum | 24 +++++++++--------- pkg/storage/unified/apistore/go.mod | 14 +++++------ pkg/storage/unified/apistore/go.sum | 28 ++++++++++----------- pkg/storage/unified/resource/go.mod | 12 ++++----- pkg/storage/unified/resource/go.sum | 24 +++++++++--------- 17 files changed, 174 insertions(+), 159 deletions(-) diff --git a/go.mod b/go.mod index d0041802abc..7c53c7a2e69 100644 --- a/go.mod +++ b/go.mod @@ -89,7 +89,7 @@ require ( github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.1.0 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group - github.com/grafana/grafana-plugin-sdk-go v0.246.0 // @grafana/plugins-platform-backend + github.com/grafana/grafana-plugin-sdk-go v0.247.0 // @grafana/plugins-platform-backend github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad @@ -138,7 +138,7 @@ require ( github.com/openfga/openfga v1.5.4 // @grafana/identity-access-team github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend - github.com/prometheus/client_golang v1.20.2 // @grafana/alerting-backend + github.com/prometheus/client_golang v1.20.3 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group github.com/prometheus/common v0.55.0 // @grafana/alerting-backend github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 // @grafana/alerting-backend @@ -172,18 +172,18 @@ require ( go.uber.org/atomic v1.11.0 // @grafana/alerting-backend go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage gocloud.dev v0.39.0 // @grafana/grafana-app-platform-squad - golang.org/x/crypto v0.26.0 // @grafana/grafana-backend-group + golang.org/x/crypto v0.27.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // @grafana/alerting-backend golang.org/x/mod v0.19.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.28.0 // @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/net v0.29.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.22.0 // @grafana/identity-access-team golang.org/x/sync v0.8.0 // @grafana/alerting-backend - golang.org/x/text v0.17.0 // @grafana/grafana-backend-group + golang.org/x/text v0.18.0 // @grafana/grafana-backend-group golang.org/x/time v0.6.0 // @grafana/grafana-backend-group golang.org/x/tools v0.23.0 // @grafana/grafana-as-code gonum.org/v1/gonum v0.14.0 // @grafana/observability-metrics google.golang.org/api v0.191.0 // @grafana/grafana-backend-group - google.golang.org/grpc v1.65.0 // @grafana/plugins-platform-backend + google.golang.org/grpc v1.66.0 // @grafana/plugins-platform-backend google.golang.org/protobuf v1.34.2 // @grafana/plugins-platform-backend gopkg.in/ini.v1 v1.67.0 // @grafana/alerting-backend gopkg.in/mail.v2 v2.3.1 // @grafana/grafana-backend-group @@ -439,8 +439,8 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // @grafana/identity-access-team - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect; @grafana/grafana-backend-group google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect @@ -485,7 +485,7 @@ require ( github.com/hairyhenderson/go-which v0.2.0 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/maypok86/otter v1.2.2 // indirect - github.com/planetscale/vtprotobuf v0.6.0 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect ) diff --git a/go.sum b/go.sum index 83d51d9fdfc..a09176aa7d2 100644 --- a/go.sum +++ b/go.sum @@ -1852,8 +1852,9 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= @@ -2291,8 +2292,8 @@ github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkr github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs= github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ= github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= -github.com/grafana/grafana-plugin-sdk-go v0.246.0 h1:id7MPpONfZhSlhE5K4LU1rBqaVcpe2EDqqPdyb7UTfw= -github.com/grafana/grafana-plugin-sdk-go v0.246.0/go.mod h1:bK7/yelc8cANvNzXPbXk64I4qkqeyQfnyIx4U9y/ItY= +github.com/grafana/grafana-plugin-sdk-go v0.247.0 h1:QOxVw3sgaPaxR3odu9DSRNGdCknyAuMWl0hDhaF91OA= +github.com/grafana/grafana-plugin-sdk-go v0.247.0/go.mod h1:sEi0wRVTvpxyB0KaMNbhPM74OnDMwVpah97usm6QXEM= github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 h1:2H9x4q53pkfUGtSNYD1qSBpNnxrFgylof/TYADb5xMI= github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2/go.mod h1:gBLBniiSUQvyt4LRrpIeysj8Many0DV+hdUKifRE0Ec= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o= @@ -2942,8 +2943,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA= -github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -2970,8 +2971,8 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -3478,8 +3479,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -3656,8 +3657,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -3867,8 +3868,8 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3892,8 +3893,8 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3915,8 +3916,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -4453,8 +4454,8 @@ google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJai google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/go.work.sum b/go.work.sum index 60d634aa36a..49ff8ee90b4 100644 --- a/go.work.sum +++ b/go.work.sum @@ -387,6 +387,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -461,10 +462,13 @@ github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= +github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/expr-lang/expr v1.16.2 h1:JvMnzUs3LeVHBvGFcXYmXo+Q6DPDmzrlcSBO6Wy3w4s= github.com/expr-lang/expr v1.16.2/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= @@ -475,6 +479,7 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= @@ -494,6 +499,7 @@ github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 h1:NxXI5pTAtpEaU49bpLpQoDsu1zrteW/vxzTz8Cd2UAs= github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -567,6 +573,7 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/influxdata/influxdb v1.7.6 h1:8mQ7A/V+3noMGCt/P9pD09ISaiz9XvgCk303UYA3gcs= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= @@ -741,6 +748,8 @@ github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwy github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= @@ -965,6 +974,7 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= @@ -976,15 +986,18 @@ gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf h1:T4tsZBlZYXK3j40sQNP5MBO32I+rn6ypV1PpklsiV8k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:5/MT647Cn/GGhwTpXC7QqcaR5Cnee4v4MKCU1/nwnIQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= @@ -998,6 +1011,7 @@ gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index ff006c52c53..f19146c4875 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -4,7 +4,7 @@ go 1.23.1 require ( github.com/emicklei/go-restful/v3 v3.11.0 - github.com/grafana/grafana-plugin-sdk-go v0.246.0 + github.com/grafana/grafana-plugin-sdk-go v0.247.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435 github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 @@ -98,7 +98,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_golang v1.20.3 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -131,15 +131,15 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect @@ -147,7 +147,7 @@ require ( google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index b0ead5bad54..c0f933a7559 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -130,8 +130,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/grafana-plugin-sdk-go v0.246.0 h1:id7MPpONfZhSlhE5K4LU1rBqaVcpe2EDqqPdyb7UTfw= -github.com/grafana/grafana-plugin-sdk-go v0.246.0/go.mod h1:bK7/yelc8cANvNzXPbXk64I4qkqeyQfnyIx4U9y/ItY= +github.com/grafana/grafana-plugin-sdk-go v0.247.0 h1:QOxVw3sgaPaxR3odu9DSRNGdCknyAuMWl0hDhaF91OA= +github.com/grafana/grafana-plugin-sdk-go v0.247.0/go.mod h1:sEi0wRVTvpxyB0KaMNbhPM74OnDMwVpah97usm6QXEM= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435 h1:lmw60EW7JWlAEvgggktOyVkH4hF1m/+LSF/Ap0NCyi8= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435/go.mod h1:ORVFiW/KNRY52lNjkGwnFWCxNVfE97bJG2jr2fetq0I= github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 h1:SNEeqY22DrGr5E9kGF1mKSqlOom14W9+b1u4XEGJowA= @@ -247,8 +247,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -384,8 +384,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -406,8 +406,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -434,14 +434,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -482,8 +482,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index 867003eb960..66ed47e5c30 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -32,13 +32,13 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 5a9f032522b..3e2cde7b9e9 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -73,8 +73,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -87,8 +87,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -106,8 +106,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -119,8 +119,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -133,8 +133,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 76f7b3fbfe7..e96e73e595e 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 - github.com/prometheus/client_golang v1.20.2 + github.com/prometheus/client_golang v1.20.3 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel/trace v1.29.0 k8s.io/apimachinery v0.31.0 @@ -72,16 +72,16 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 8b5c93609f4..39bfac54ea2 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -132,8 +132,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -219,8 +219,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -239,8 +239,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -255,14 +255,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -298,8 +298,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 227dc61a68e..5764f07f08d 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -36,16 +36,16 @@ require ( go.opentelemetry.io/otel v1.29.0 // indirect; @grafana/grafana-backend-group go.opentelemetry.io/otel/sdk v1.29.0 // indirect; @grafana/grafana-backend-group go.opentelemetry.io/otel/trace v1.29.0 // indirect; @grafana/grafana-backend-group - golang.org/x/crypto v0.26.0 // indirect; @grafana/grafana-backend-group + golang.org/x/crypto v0.27.0 // indirect; @grafana/grafana-backend-group golang.org/x/mod v0.19.0 // @grafana/grafana-backend-group - golang.org/x/net v0.28.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/net v0.29.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.22.0 // @grafana/identity-access-team golang.org/x/sync v0.8.0 // indirect; @grafana/alerting-backend - golang.org/x/text v0.17.0 // indirect; @grafana/grafana-backend-group + golang.org/x/text v0.18.0 // indirect; @grafana/grafana-backend-group golang.org/x/time v0.6.0 // indirect; @grafana/grafana-backend-group golang.org/x/tools v0.23.0 // indirect; @grafana/grafana-as-code google.golang.org/api v0.191.0 // @grafana/grafana-backend-group - google.golang.org/grpc v1.65.0 // indirect; @grafana/plugins-platform-backend + google.golang.org/grpc v1.66.0 // indirect; @grafana/plugins-platform-backend google.golang.org/protobuf v1.34.2 // indirect; @grafana/plugins-platform-backend gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-backend ) @@ -85,7 +85,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.25.0 // indirect google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect; @grafana/grafana-backend-group google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 3e13a2b8397..433f156f404 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -263,8 +263,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -284,8 +284,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -306,13 +306,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= @@ -349,8 +349,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index ceb58742f58..e225f79ea7a 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -4,10 +4,10 @@ go 1.23.1 require ( github.com/grafana/dskit v0.0.0-20240805174438-dfa83b4ed2d3 - github.com/grafana/grafana-plugin-sdk-go v0.246.0 + github.com/grafana/grafana-plugin-sdk-go v0.247.0 github.com/json-iterator/go v1.1.12 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.20.2 + github.com/prometheus/client_golang v1.20.3 github.com/prometheus/common v0.55.0 github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 github.com/stretchr/testify v1.9.0 @@ -113,16 +113,16 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/tools v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 155a8e1f46f..c727066713a 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -98,8 +98,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/grafana/dskit v0.0.0-20240805174438-dfa83b4ed2d3 h1:as4PmrFoYI1byS5JjsgPC7uSGTMh+SgS0ePv6hOyDGU= github.com/grafana/dskit v0.0.0-20240805174438-dfa83b4ed2d3/go.mod h1:lcjGB6SuaZ2o44A9nD6p/tR4QXSPbzViRY520Gy6pTQ= -github.com/grafana/grafana-plugin-sdk-go v0.246.0 h1:id7MPpONfZhSlhE5K4LU1rBqaVcpe2EDqqPdyb7UTfw= -github.com/grafana/grafana-plugin-sdk-go v0.246.0/go.mod h1:bK7/yelc8cANvNzXPbXk64I4qkqeyQfnyIx4U9y/ItY= +github.com/grafana/grafana-plugin-sdk-go v0.247.0 h1:QOxVw3sgaPaxR3odu9DSRNGdCknyAuMWl0hDhaF91OA= +github.com/grafana/grafana-plugin-sdk-go v0.247.0/go.mod h1:sEi0wRVTvpxyB0KaMNbhPM74OnDMwVpah97usm6QXEM= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= @@ -199,8 +199,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= @@ -309,8 +309,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -331,12 +331,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -356,8 +356,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1: google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 82a6ea30b81..cdb401dd379 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -9,7 +9,7 @@ require ( github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d github.com/stretchr/testify v1.9.0 gocloud.dev v0.39.0 - google.golang.org/grpc v1.65.0 + google.golang.org/grpc v1.66.0 k8s.io/apimachinery v0.31.0 k8s.io/apiserver v0.31.0 k8s.io/client-go v0.31.0 @@ -59,7 +59,7 @@ require ( github.com/onsi/gomega v1.34.1 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_golang v1.20.3 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -80,13 +80,13 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/api v0.191.0 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 7e43c070bc5..9eb389205ce 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -230,8 +230,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -320,8 +320,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -347,8 +347,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -373,15 +373,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -389,8 +389,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -432,8 +432,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index f12630c30b8..a8a0a945649 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -8,11 +8,11 @@ require ( github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 - github.com/prometheus/client_golang v1.20.2 + github.com/prometheus/client_golang v1.20.3 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel/trace v1.29.0 gocloud.dev v0.39.0 - google.golang.org/grpc v1.65.0 + google.golang.org/grpc v1.66.0 google.golang.org/protobuf v1.34.2 k8s.io/apimachinery v0.31.0 ) @@ -43,11 +43,11 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.29.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/api v0.191.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 1246b0a8b1c..de9f59a2b26 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -169,8 +169,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= @@ -217,8 +217,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -241,8 +241,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -267,8 +267,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -281,8 +281,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -320,8 +320,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From ba9f1da28e2b68441a52d8bc5759332c47ff2b7e Mon Sep 17 00:00:00 2001 From: Ieva Date: Mon, 9 Sep 2024 12:50:09 +0100 Subject: [PATCH 07/18] Data sources: Hide the datasource redirection banner for users who can't interact with data sources (#92886) * hide the datasource redirection banner for users who can't list data sources * readding explore evaluation * add a test --- .../ConnectionsRedirectNotice.test.tsx | 23 +++++++++++++++++++ .../ConnectionsRedirectNotice.tsx | 7 +++++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.test.tsx diff --git a/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.test.tsx b/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.test.tsx new file mode 100644 index 00000000000..a6c435be744 --- /dev/null +++ b/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from '@testing-library/react'; + +import { contextSrv } from '../../../../core/services/context_srv'; + +import { ConnectionsRedirectNotice } from './ConnectionsRedirectNotice'; + +const setup = (hasAccessToDS: boolean) => { + jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(hasAccessToDS); + + render(); +}; + +describe('ConnectionsRedirectNotice', () => { + it('should render component when has access to data sources', () => { + setup(true); + expect(screen.getByRole('link')).toBeInTheDocument(); + }); + + it('should not render component when has no access to data sources', () => { + setup(false); + expect(screen.queryByRole('link')).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx b/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx index c5aabc6a2f3..a27534f5baf 100644 --- a/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx +++ b/public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx @@ -4,6 +4,8 @@ import { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, LinkButton, useStyles2 } from '@grafana/ui'; +import { contextSrv } from '../../../../core/core'; +import { AccessControlAction } from '../../../../types'; import { ROUTES } from '../../constants'; const getStyles = (theme: GrafanaTheme2) => ({ @@ -22,7 +24,10 @@ const getStyles = (theme: GrafanaTheme2) => ({ export function ConnectionsRedirectNotice() { const styles = useStyles2(getStyles); - const [showNotice, setShowNotice] = useState(true); + const canAccessDataSources = + contextSrv.hasPermission(AccessControlAction.DataSourcesCreate) || + contextSrv.hasPermission(AccessControlAction.DataSourcesWrite); + const [showNotice, setShowNotice] = useState(canAccessDataSources); return showNotice ? ( setShowNotice(false)}> From 5e2ac24890906e5070323d87730dd78a4f885963 Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Mon, 9 Sep 2024 14:45:05 +0200 Subject: [PATCH 08/18] Sidecar: Add split view and basic APIs for extensions (#91648) * Add split view and basic APIs to extensions * Add comments * Update public/app/AppWrapper.tsx Co-authored-by: Levente Balogh * Moved the .grafana-app element and deduplicate some code * Remove the provider variants of usePluginLinks/Components * Change buildPluginSectionNav * Update comment * Use eventBus * Remove non existent exports * refactor: use a sidecar service to encapsulate the state * Don't wrap single app in split wrapper * Use hook splitter * Remove inline styles * Type the style props from useSplitter * Move the overflow style changes to appWrapper * Deduplicate some common top level providers * Move modals * Move routes wrappers to it's own file * Use better css and add comments * Remove query rows app extension point * Fix test --------- Co-authored-by: Levente Balogh --- .../feature-toggles/index.md | 1 + .../src/types/featureToggles.gen.ts | 1 + .../src/types/pluginExtensions.ts | 23 +- .../pluginExtensions/getPluginExtensions.ts | 1 + .../src/components/Splitter/useSplitter.ts | 20 +- pkg/services/featuremgmt/registry.go | 6 + pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.go | 4 + pkg/services/featuremgmt/toggles_gen.json | 14 +- public/app/AppWrapper.tsx | 66 +++--- public/app/core/context/SidecarContext.ts | 22 ++ public/app/core/services/SidecarService.ts | 74 +++++++ .../getExploreExtensionConfigs.test.tsx | 4 +- .../plugins/components/AppRootPage.tsx | 7 +- .../extensions/getPluginExtensions.test.tsx | 5 +- .../plugins/extensions/getPluginExtensions.ts | 12 +- .../extensions/usePluginExtensions.tsx | 7 + .../plugins/extensions/usePluginLinks.tsx | 6 +- .../plugins/extensions/utils.test.tsx | 197 +++++++++--------- .../app/features/plugins/extensions/utils.tsx | 31 ++- .../plugins/extensions/validators.test.tsx | 2 +- public/app/features/plugins/utils.test.ts | 13 +- public/app/features/plugins/utils.ts | 9 +- public/app/routes/RoutesWrapper.tsx | 150 +++++++++++++ 24 files changed, 480 insertions(+), 196 deletions(-) create mode 100644 public/app/core/context/SidecarContext.ts create mode 100644 public/app/core/services/SidecarService.ts create mode 100644 public/app/routes/RoutesWrapper.tsx diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 150b4273f92..d50f0bb7f26 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -198,6 +198,7 @@ Experimental features might be changed or removed without prior notice. | `exploreLogsShardSplitting` | Used in Explore Logs to split queries into multiple queries based on the number of shards | | `exploreLogsAggregatedMetrics` | Used in Explore Logs to query by aggregated metrics | | `exploreLogsLimitedTimeRange` | Used in Explore Logs to limit the time range | +| `appSidecar` | Enable the app sidecar feature that allows rendering 2 apps at the same time | ## Development feature toggles diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index c79f0418045..27eeb7769cc 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -208,4 +208,5 @@ export interface FeatureToggles { exploreLogsAggregatedMetrics?: boolean; exploreLogsLimitedTimeRange?: boolean; appPlatformAccessTokens?: boolean; + appSidecar?: boolean; } diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index bdb3e06b100..54c1170681f 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -75,7 +75,10 @@ export type PluginExtensionAddedComponentConfig = PluginExtensionCon component: React.ComponentType; }; -export type PluginAddedLinksConfigureFunc = (context?: Readonly) => +export type PluginAddedLinksConfigureFunc = ( + context: Readonly | undefined, + helpers: PluginExtensionHelpers +) => | Partial<{ title: string; description: string; @@ -142,11 +145,27 @@ export type PluginExtensionOpenModalOptions = { height?: string | number; }; +type PluginExtensionHelpers = { + /** Checks if the app plugin (that registers the extension) is currently visible (either in the main view or in the side view) + * @experimental + */ + isAppOpened: () => boolean; +}; + export type PluginExtensionEventHelpers = { context?: Readonly; // Opens a modal dialog and renders the provided React component inside it openModal: (options: PluginExtensionOpenModalOptions) => void; -}; + + /** Opens the app plugin (that registers the extensions) in a side view + * @experimental + */ + openAppInSideview: (context?: unknown) => void; + /** Closes the side view for the app plugin (that registers the extensions) in case it was open + * @experimental + */ + closeAppInSideview: () => void; +} & PluginExtensionHelpers; // Extension Points & Contexts // -------------------------------------------------------- diff --git a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts index cf6d48d7e6b..be151b05858 100644 --- a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts +++ b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts @@ -12,6 +12,7 @@ export type UsePluginExtensions = ( export type GetPluginExtensionsOptions = { extensionPointId: string; + // Make sure this object is properly memoized and not mutated. context?: object | Record; limitPerPlugin?: number; }; diff --git a/packages/grafana-ui/src/components/Splitter/useSplitter.ts b/packages/grafana-ui/src/components/Splitter/useSplitter.ts index 0ba04256876..325087bf683 100644 --- a/packages/grafana-ui/src/components/Splitter/useSplitter.ts +++ b/packages/grafana-ui/src/components/Splitter/useSplitter.ts @@ -300,6 +300,16 @@ export function useSplitter(options: UseSplitterOptions) { const dragHandleStyle = direction === 'column' ? dragStyles.dragHandleHorizontal : dragStyles.dragHandleVertical; const id = useId(); + const primaryStyles: React.CSSProperties = { + flexGrow: clamp(initialSize ?? 0.5, 0, 1), + [minDimProp]: 'min-content', + }; + + const secondaryStyles: React.CSSProperties = { + flexGrow: clamp(1 - initialSize, 0, 1), + [minDimProp]: 'min-content', + }; + return { containerProps: { ref: containerRef, @@ -308,18 +318,12 @@ export function useSplitter(options: UseSplitterOptions) { primaryProps: { ref: firstPaneRef, className: styles.panel, - style: { - [minDimProp]: 'min-content', - flexGrow: clamp(initialSize ?? 0.5, 0, 1), - }, + style: primaryStyles, }, secondaryProps: { ref: secondPaneRef, className: styles.panel, - style: { - flexGrow: clamp(1 - initialSize, 0, 1), - [minDimProp]: 'min-content', - }, + style: secondaryStyles, }, splitterProps: { onPointerUp, diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index a7f645e3aa2..b4eae84b66a 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1434,6 +1434,12 @@ var ( HideFromDocs: true, HideFromAdminPage: true, }, + { + Name: "appSidecar", + Description: "Enable the app sidecar feature that allows rendering 2 apps at the same time", + Stage: FeatureStageExperimental, + Owner: grafanaExploreSquad, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 0ad5a6f19a1..7cf0306e768 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -189,3 +189,4 @@ exploreLogsShardSplitting,experimental,@grafana/observability-logs,false,false,t exploreLogsAggregatedMetrics,experimental,@grafana/observability-logs,false,false,true exploreLogsLimitedTimeRange,experimental,@grafana/observability-logs,false,false,true appPlatformAccessTokens,experimental,@grafana/identity-access-team,false,false,false +appSidecar,experimental,@grafana/explore-squad,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 544f2d40fd7..e0a5c7415c4 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -766,4 +766,8 @@ const ( // FlagAppPlatformAccessTokens // Enables the use of access tokens for the App Platform FlagAppPlatformAccessTokens = "appPlatformAccessTokens" + + // FlagAppSidecar + // Enable the app sidecar feature that allows rendering 2 apps at the same time + FlagAppSidecar = "appSidecar" ) diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 46e9189f4c6..8e6f4795d46 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -350,6 +350,18 @@ "hideFromDocs": true } }, + { + "metadata": { + "name": "appSidecar", + "resourceVersion": "1722872007883", + "creationTimestamp": "2024-08-05T15:33:27Z" + }, + "spec": { + "description": "Enable the app sidecar feature that allows rendering 2 apps at the same time", + "stage": "experimental", + "codeowner": "@grafana/explore-squad" + } + }, { "metadata": { "name": "authAPIAccessTokenAuth", @@ -2859,4 +2871,4 @@ } } ] -} \ No newline at end of file +} diff --git a/public/app/AppWrapper.tsx b/public/app/AppWrapper.tsx index b73839203c2..0cbe1f21d78 100644 --- a/public/app/AppWrapper.tsx +++ b/public/app/AppWrapper.tsx @@ -1,32 +1,25 @@ import { Action, KBarProvider } from 'kbar'; import { Component, ComponentType } from 'react'; import { Provider } from 'react-redux'; -import { Router, Redirect, Switch, RouteComponentProps } from 'react-router-dom'; -import { CompatRouter, CompatRoute } from 'react-router-dom-v5-compat'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom'; +import { CompatRoute } from 'react-router-dom-v5-compat'; -import { - config, - locationService, - LocationServiceProvider, - navigationLogger, - reportInteraction, -} from '@grafana/runtime'; -import { ErrorBoundaryAlert, GlobalStyles, ModalRoot, PortalContainer, Stack } from '@grafana/ui'; +import { config, navigationLogger, reportInteraction } from '@grafana/runtime'; +import { ErrorBoundaryAlert, GlobalStyles, PortalContainer } from '@grafana/ui'; import { getAppRoutes } from 'app/routes/routes'; import { store } from 'app/store/store'; -import { AngularRoot } from './angular/AngularRoot'; import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled'; import { GrafanaApp } from './app'; -import { AppChrome } from './core/components/AppChrome/AppChrome'; -import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList'; import { GrafanaContext } from './core/context/GrafanaContext'; -import { ModalsContextProvider } from './core/context/ModalsContextProvider'; +import { SidecarContext } from './core/context/SidecarContext'; import { GrafanaRoute } from './core/navigation/GrafanaRoute'; import { RouteDescriptor } from './core/navigation/types'; +import { sidecarService } from './core/services/SidecarService'; import { contextSrv } from './core/services/context_srv'; import { ThemeProvider } from './core/utils/ConfigProvider'; import { LiveConnectionWarning } from './features/live/LiveConnectionWarning'; +import { ExperimentalSplitPaneRouterWrapper, RouterWrapper } from './routes/RoutesWrapper'; interface AppWrapperProps { app: GrafanaApp; @@ -100,6 +93,12 @@ export class AppWrapper extends Component { }); }; + const routerWrapperProps = { + routes: ready && this.renderRoutes(), + pageBanners, + bodyRenderHooks, + }; + return ( @@ -109,33 +108,18 @@ export class AppWrapper extends Component { actions={[]} options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }} > - - - - - -
- - - - - {pageBanners.map((Banner, index) => ( - - ))} - {ready && this.renderRoutes()} - - {bodyRenderHooks.map((Hook, index) => ( - - ))} - -
- - - -
-
-
-
+ + +
+ {config.featureToggles.appSidecar ? ( + + ) : ( + + )} + + +
+
diff --git a/public/app/core/context/SidecarContext.ts b/public/app/core/context/SidecarContext.ts new file mode 100644 index 00000000000..c6b341295c6 --- /dev/null +++ b/public/app/core/context/SidecarContext.ts @@ -0,0 +1,22 @@ +import { createContext, useContext } from 'react'; +import { useObservable } from 'react-use'; + +import { SidecarService, sidecarService } from '../services/SidecarService'; + +export const SidecarContext = createContext(sidecarService); + +export function useSidecar() { + const activePluginId = useObservable(sidecarService.activePluginId); + const context = useContext(SidecarContext); + + if (!context) { + throw new Error('No SidecarContext found'); + } + + return { + activePluginId, + openApp: (pluginId: string) => context.openApp(pluginId), + closeApp: (pluginId: string) => context.closeApp(pluginId), + isAppOpened: (pluginId: string) => context.isAppOpened(pluginId), + }; +} diff --git a/public/app/core/services/SidecarService.ts b/public/app/core/services/SidecarService.ts new file mode 100644 index 00000000000..08005da697b --- /dev/null +++ b/public/app/core/services/SidecarService.ts @@ -0,0 +1,74 @@ +import { BehaviorSubject } from 'rxjs'; + +import { config, locationService } from '@grafana/runtime'; + +export class SidecarService { + // The ID of the app plugin that is currently opened in the sidecar view + private _activePluginId: BehaviorSubject; + + constructor() { + this._activePluginId = new BehaviorSubject(undefined); + } + + private assertFeatureEnabled() { + if (!config.featureToggles.appSidecar) { + console.warn('The `appSidecar` feature toggle is not enabled, doing nothing.'); + return false; + } + + return true; + } + + get activePluginId() { + return this._activePluginId.asObservable(); + } + + openApp(pluginId: string) { + if (!this.assertFeatureEnabled()) { + return false; + } + + return this._activePluginId.next(pluginId); + } + + closeApp(pluginId: string) { + if (!this.assertFeatureEnabled()) { + return false; + } + + if (this._activePluginId.getValue() === pluginId) { + return this._activePluginId.next(undefined); + } + } + + isAppOpened(pluginId: string) { + if (!this.assertFeatureEnabled()) { + return false; + } + + if (this._activePluginId.getValue() === pluginId || getMainAppPluginId() === pluginId) { + return true; + } + + return false; + } +} + +export const sidecarService = new SidecarService(); + +// The app plugin that is "open" in the main Grafana view +function getMainAppPluginId() { + const { pathname } = locationService.getLocation(); + + // A naive way to sort of simulate core features being an app and having an appID + let mainApp = pathname.match(/\/a\/([^/]+)/)?.[1]; + if (!mainApp && pathname.match(/\/explore/)) { + mainApp = 'explore'; + } + + if (!mainApp && pathname.match(/\/d\//)) { + mainApp = 'dashboards'; + } + + return mainApp || 'unknown'; +} diff --git a/public/app/features/explore/extensions/getExploreExtensionConfigs.test.tsx b/public/app/features/explore/extensions/getExploreExtensionConfigs.test.tsx index 5d197f79dde..869a00d8006 100644 --- a/public/app/features/explore/extensions/getExploreExtensionConfigs.test.tsx +++ b/public/app/features/explore/extensions/getExploreExtensionConfigs.test.tsx @@ -43,7 +43,7 @@ describe('getExploreExtensionConfigs', () => { const extensions = getExploreExtensionConfigs(); const [extension] = extensions; - expect(extension?.configure?.()).toBeUndefined(); + expect(extension?.configure?.(undefined, { isAppOpened: () => false })).toBeUndefined(); }); it('should return empty object if sufficient permissions', () => { @@ -52,7 +52,7 @@ describe('getExploreExtensionConfigs', () => { const extensions = getExploreExtensionConfigs(); const [extension] = extensions; - expect(extension?.configure?.()).toEqual({}); + expect(extension?.configure?.(undefined, { isAppOpened: () => false })).toEqual({}); }); }); }); diff --git a/public/app/features/plugins/components/AppRootPage.tsx b/public/app/features/plugins/components/AppRootPage.tsx index aa1d46b7491..c01bb2f4941 100644 --- a/public/app/features/plugins/components/AppRootPage.tsx +++ b/public/app/features/plugins/components/AppRootPage.tsx @@ -33,8 +33,9 @@ import { buildPluginPageContext, PluginPageContext } from './PluginPageContext'; interface Props { // The ID of the plugin we would like to load and display pluginId: string; - // The root navModelItem for the plugin (root = lives directly under 'home') - pluginNavSection: NavModelItem; + // The root navModelItem for the plugin (root = lives directly under 'home'). In case app does not need a nva model, + // for example it's in some way embedded or shown in a sideview this can be undefined. + pluginNavSection?: NavModelItem; } interface State { @@ -53,7 +54,7 @@ export function AppRootPage({ pluginId, pluginNavSection }: Props) { const [state, dispatch] = useReducer(stateSlice.reducer, initialState); const currentUrl = config.appSubUrl + location.pathname + location.search; const { plugin, loading, loadingError, pluginNav } = state; - const navModel = buildPluginSectionNav(pluginNavSection, pluginNav, currentUrl); + const navModel = buildPluginSectionNav(currentUrl, pluginNavSection); const queryParams = useMemo(() => locationSearchToObject(location.search), [location.search]); const context = useMemo(() => buildPluginPageContext(navModel), [navModel]); const grafanaContext = useGrafana(); diff --git a/public/app/features/plugins/extensions/getPluginExtensions.test.tsx b/public/app/features/plugins/extensions/getPluginExtensions.test.tsx index b3af3582ea7..23d44c6f540 100644 --- a/public/app/features/plugins/extensions/getPluginExtensions.test.tsx +++ b/public/app/features/plugins/extensions/getPluginExtensions.test.tsx @@ -174,7 +174,10 @@ describe('getPluginExtensions()', () => { getPluginExtensions({ ...registries, context, extensionPointId: extensionPoint2 }); expect(link2.configure).toHaveBeenCalledTimes(1); - expect(link2.configure).toHaveBeenCalledWith(context); + expect(link2.configure).toHaveBeenCalledWith( + context, + expect.objectContaining({ isAppOpened: expect.any(Function) }) + ); }); test('should be possible to update the basic properties with the configure() function', async () => { diff --git a/public/app/features/plugins/extensions/getPluginExtensions.ts b/public/app/features/plugins/extensions/getPluginExtensions.ts index 241f26ca15f..bd7d55d6e0d 100644 --- a/public/app/features/plugins/extensions/getPluginExtensions.ts +++ b/public/app/features/plugins/extensions/getPluginExtensions.ts @@ -88,11 +88,7 @@ export const getPluginExtensions: GetExtensions = ({ const path = overrides?.path || addedLink.path; const extension: PluginExtensionLink = { - id: generateExtensionId(pluginId, { - ...addedLink, - extensionPointId, - type: PluginExtensionTypes.link, - }), + id: generateExtensionId(pluginId, extensionPointId, addedLink.title), type: PluginExtensionTypes.link, pluginId: pluginId, onClick: getLinkExtensionOnClick(pluginId, extensionPointId, addedLink, frozenContext), @@ -125,11 +121,7 @@ export const getPluginExtensions: GetExtensions = ({ extensionsByPlugin[addedComponent.pluginId] = 0; } const extension: PluginExtensionComponent = { - id: generateExtensionId(addedComponent.pluginId, { - ...addedComponent, - extensionPointId, - type: PluginExtensionTypes.component, - }), + id: generateExtensionId(addedComponent.pluginId, extensionPointId, addedComponent.title), type: PluginExtensionTypes.component, pluginId: addedComponent.pluginId, title: addedComponent.title, diff --git a/public/app/features/plugins/extensions/usePluginExtensions.tsx b/public/app/features/plugins/extensions/usePluginExtensions.tsx index 62e583ad857..f67c24ba5c5 100644 --- a/public/app/features/plugins/extensions/usePluginExtensions.tsx +++ b/public/app/features/plugins/extensions/usePluginExtensions.tsx @@ -3,6 +3,7 @@ import { useObservable } from 'react-use'; import { PluginExtension } from '@grafana/data'; import { GetPluginExtensionsOptions, UsePluginExtensionsResult } from '@grafana/runtime'; +import { useSidecar } from 'app/core/context/SidecarContext'; import { getPluginExtensions } from './getPluginExtensions'; import { PluginExtensionRegistries } from './registry/types'; @@ -14,6 +15,7 @@ export function createUsePluginExtensions(registries: PluginExtensionRegistries) return function usePluginExtensions(options: GetPluginExtensionsOptions): UsePluginExtensionsResult { const addedComponentsRegistry = useObservable(observableAddedComponentsRegistry); const addedLinksRegistry = useObservable(observableAddedLinksRegistry); + const { activePluginId } = useSidecar(); const { extensions } = useMemo(() => { if (!addedLinksRegistry && !addedComponentsRegistry) { @@ -27,12 +29,17 @@ export function createUsePluginExtensions(registries: PluginExtensionRegistries) addedComponentsRegistry, addedLinksRegistry, }); + // Doing the deps like this instead of just `option` because users probably aren't going to memoize the + // options object so we are checking it's simple value attributes. + // The context though still has to be memoized though and not mutated. + // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: refactor `getPluginExtensions` to accept service dependencies as arguments instead of relying on the sidecar singleton under the hood }, [ addedLinksRegistry, addedComponentsRegistry, options.extensionPointId, options.context, options.limitPerPlugin, + activePluginId, ]); return { extensions, isLoading: false }; diff --git a/public/app/features/plugins/extensions/usePluginLinks.tsx b/public/app/features/plugins/extensions/usePluginLinks.tsx index 0350209636a..be8f3f5bf03 100644 --- a/public/app/features/plugins/extensions/usePluginLinks.tsx +++ b/public/app/features/plugins/extensions/usePluginLinks.tsx @@ -60,11 +60,7 @@ export function createUsePluginLinks(registry: AddedLinksRegistry) { const path = overrides?.path || addedLink.path; const extension: PluginExtensionLink = { - id: generateExtensionId(pluginId, { - ...addedLink, - extensionPointId, - type: PluginExtensionTypes.link, - }), + id: generateExtensionId(pluginId, extensionPointId, addedLink.title), type: PluginExtensionTypes.link, pluginId: pluginId, onClick: getLinkExtensionOnClick(pluginId, extensionPointId, addedLink, frozenContext), diff --git a/public/app/features/plugins/extensions/utils.test.tsx b/public/app/features/plugins/extensions/utils.test.tsx index f579138e76a..aca0f0797cf 100644 --- a/public/app/features/plugins/extensions/utils.test.tsx +++ b/public/app/features/plugins/extensions/utils.test.tsx @@ -5,7 +5,13 @@ import { dateTime, usePluginContext } from '@grafana/data'; import appEvents from 'app/core/app_events'; import { ShowModalReactEvent } from 'app/types/events'; -import { deepFreeze, handleErrorsInFn, getReadOnlyProxy, getEventHelpers, wrapWithPluginContext } from './utils'; +import { + deepFreeze, + handleErrorsInFn, + getReadOnlyProxy, + createOpenModalFunction, + wrapWithPluginContext, +} from './utils'; jest.mock('app/features/plugins/pluginSettings', () => ({ ...jest.requireActual('app/features/plugins/pluginSettings'), @@ -306,111 +312,100 @@ describe('Plugin Extensions / Utils', () => { }); }); - describe('getEventHelpers', () => { - describe('openModal', () => { - let renderModalSubscription: Unsubscribable | undefined; + describe('createOpenModalFunction()', () => { + let renderModalSubscription: Unsubscribable | undefined; - beforeAll(() => { - renderModalSubscription = appEvents.subscribe(ShowModalReactEvent, (event) => { - const { payload } = event; - const Modal = payload.component; - render(); - }); - }); - - afterAll(() => { - renderModalSubscription?.unsubscribe(); - }); - - it('should open modal with provided title and body', async () => { - const pluginId = 'grafana-worldmap-panel'; - const { openModal } = getEventHelpers(pluginId); - - openModal({ - title: 'Title in modal', - body: () =>
Text in body
, - }); - - expect(await screen.findByRole('dialog')).toBeVisible(); - expect(screen.getByRole('heading')).toHaveTextContent('Title in modal'); - expect(screen.getByText('Text in body')).toBeVisible(); - }); - - it('should open modal with default width if not specified', async () => { - const pluginId = 'grafana-worldmap-panel'; - const { openModal } = getEventHelpers(pluginId); - - openModal({ - title: 'Title in modal', - body: () =>
Text in body
, - }); - - const modal = await screen.findByRole('dialog'); - const style = window.getComputedStyle(modal); - - expect(style.width).toBe('750px'); - expect(style.height).toBe(''); - }); - - it('should open modal with specified width', async () => { - const pluginId = 'grafana-worldmap-panel'; - const { openModal } = getEventHelpers(pluginId); - - openModal({ - title: 'Title in modal', - body: () =>
Text in body
, - width: '70%', - }); - - const modal = await screen.findByRole('dialog'); - const style = window.getComputedStyle(modal); - - expect(style.width).toBe('70%'); - }); - - it('should open modal with specified height', async () => { - const pluginId = 'grafana-worldmap-panel'; - const { openModal } = getEventHelpers(pluginId); - - openModal({ - title: 'Title in modal', - body: () =>
Text in body
, - height: 600, - }); - - const modal = await screen.findByRole('dialog'); - const style = window.getComputedStyle(modal); - - expect(style.height).toBe('600px'); - }); - - it('should open modal with the plugin context being available', async () => { - const pluginId = 'grafana-worldmap-panel'; - const { openModal } = getEventHelpers(pluginId); - - const ModalContent = () => { - const context = usePluginContext(); - - return
Version: {context.meta.info.version}
; - }; - - openModal({ - title: 'Title in modal', - body: ModalContent, - }); - - const modal = await screen.findByRole('dialog'); - expect(modal).toHaveTextContent('Version: 1.0.0'); + beforeAll(() => { + renderModalSubscription = appEvents.subscribe(ShowModalReactEvent, (event) => { + const { payload } = event; + const Modal = payload.component; + render(); }); }); - describe('context', () => { - it('should return same object as passed to getEventHelpers', () => { - const pluginId = 'grafana-worldmap-panel'; - const source = {}; - const { context } = getEventHelpers(pluginId, source); - expect(context).toBe(source); + afterAll(() => { + renderModalSubscription?.unsubscribe(); + }); + + it('should open modal with provided title and body', async () => { + const pluginId = 'grafana-worldmap-panel'; + const openModal = createOpenModalFunction(pluginId); + + openModal({ + title: 'Title in modal', + body: () =>
Text in body
, }); + + expect(await screen.findByRole('dialog')).toBeVisible(); + expect(screen.getByRole('heading')).toHaveTextContent('Title in modal'); + expect(screen.getByText('Text in body')).toBeVisible(); + }); + + it('should open modal with default width if not specified', async () => { + const pluginId = 'grafana-worldmap-panel'; + const openModal = createOpenModalFunction(pluginId); + + openModal({ + title: 'Title in modal', + body: () =>
Text in body
, + }); + + const modal = await screen.findByRole('dialog'); + const style = window.getComputedStyle(modal); + + expect(style.width).toBe('750px'); + expect(style.height).toBe(''); + }); + + it('should open modal with specified width', async () => { + const pluginId = 'grafana-worldmap-panel'; + const openModal = createOpenModalFunction(pluginId); + + openModal({ + title: 'Title in modal', + body: () =>
Text in body
, + width: '70%', + }); + + const modal = await screen.findByRole('dialog'); + const style = window.getComputedStyle(modal); + + expect(style.width).toBe('70%'); + }); + + it('should open modal with specified height', async () => { + const pluginId = 'grafana-worldmap-panel'; + const openModal = createOpenModalFunction(pluginId); + + openModal({ + title: 'Title in modal', + body: () =>
Text in body
, + height: 600, + }); + + const modal = await screen.findByRole('dialog'); + const style = window.getComputedStyle(modal); + + expect(style.height).toBe('600px'); + }); + + it('should open modal with the plugin context being available', async () => { + const pluginId = 'grafana-worldmap-panel'; + const openModal = createOpenModalFunction(pluginId); + + const ModalContent = () => { + const context = usePluginContext(); + + return
Version: {context.meta.info.version}
; + }; + + openModal({ + title: 'Title in modal', + body: ModalContent, + }); + + const modal = await screen.findByRole('dialog'); + expect(modal).toHaveTextContent('Version: 1.0.0'); }); }); diff --git a/public/app/features/plugins/extensions/utils.tsx b/public/app/features/plugins/extensions/utils.tsx index 8c3075dd402..ade21ad0962 100644 --- a/public/app/features/plugins/extensions/utils.tsx +++ b/public/app/features/plugins/extensions/utils.tsx @@ -20,6 +20,8 @@ import { import { reportInteraction } from '@grafana/runtime'; import { Modal } from '@grafana/ui'; import appEvents from 'app/core/app_events'; +// TODO: instead of depending on the service as a singleton, inject it as an argument from the React context +import { sidecarService } from 'app/core/services/SidecarService'; import { getPluginSettings } from 'app/features/plugins/pluginSettings'; import { ShowModalReactEvent } from 'app/types/events'; @@ -48,9 +50,8 @@ export function handleErrorsInFn(fn: Function, errorMessagePrefix = '') { }; } -// Event helpers are designed to make it easier to trigger "core actions" from an extension event handler, e.g. opening a modal or showing a notification. -export function getEventHelpers(pluginId: string, context?: Readonly): PluginExtensionEventHelpers { - const openModal: PluginExtensionEventHelpers['openModal'] = async (options) => { +export function createOpenModalFunction(pluginId: string): PluginExtensionEventHelpers['openModal'] { + return async (options) => { const { title, body, width, height } = options; appEvents.publish( @@ -59,8 +60,6 @@ export function getEventHelpers(pluginId: string, context?: Readonly): P }) ); }; - - return { openModal, context }; } type ModalWrapperProps = { @@ -161,8 +160,8 @@ export function deepFreeze(value?: object | Record | u return Object.freeze(clonedValue); } -export function generateExtensionId(pluginId: string, extensionConfig: PluginExtensionConfig): string { - const str = `${pluginId}${extensionConfig.extensionPointId}${extensionConfig.title}`; +export function generateExtensionId(pluginId: string, extensionPointId: string, title: string): string { + const str = `${pluginId}${extensionPointId}${title}`; return Array.from(str) .reduce((s, c) => (Math.imul(31, s) + c.charCodeAt(0)) | 0, 0) @@ -298,7 +297,7 @@ export function createExtensionSubMenu(extensions: PluginExtensionLink[]): Panel export function getLinkExtensionOverrides(pluginId: string, config: AddedLinkRegistryItem, context?: object) { try { - const overrides = config.configure?.(context); + const overrides = config.configure?.(context, { isAppOpened: () => isAppOpened(pluginId) }); // Hiding the extension if (overrides === undefined) { @@ -369,7 +368,15 @@ export function getLinkExtensionOnClick( category: config.category, }); - const result = onClick(event, getEventHelpers(pluginId, context)); + const helpers: PluginExtensionEventHelpers = { + context, + openModal: createOpenModalFunction(pluginId), + isAppOpened: () => isAppOpened(pluginId), + openAppInSideview: () => openAppInSideview(pluginId), + closeAppInSideview: () => closeAppInSideview(pluginId), + }; + + const result = onClick(event, helpers); if (isPromise(result)) { result.catch((e) => { @@ -395,3 +402,9 @@ export function getLinkExtensionPathWithTracking(pluginId: string, path: string, }) ); } + +export const openAppInSideview = (pluginId: string) => sidecarService.openApp(pluginId); + +export const closeAppInSideview = (pluginId: string) => sidecarService.closeApp(pluginId); + +export const isAppOpened = (pluginId: string) => sidecarService.isAppOpened(pluginId); diff --git a/public/app/features/plugins/extensions/validators.test.tsx b/public/app/features/plugins/extensions/validators.test.tsx index c7ad7a8f271..efb329af5cf 100644 --- a/public/app/features/plugins/extensions/validators.test.tsx +++ b/public/app/features/plugins/extensions/validators.test.tsx @@ -70,7 +70,7 @@ describe('Plugin Extension Validators', () => { title: 'Title', description: 'Description', targets: 'grafana/some-page/extension-point-a', - configure: () => {}, + configure: (_, {}) => {}, } as PluginExtensionAddedLinkConfig); }).not.toThrowError(); }); diff --git a/public/app/features/plugins/utils.test.ts b/public/app/features/plugins/utils.test.ts index 349a9870a6b..533228ac99e 100644 --- a/public/app/features/plugins/utils.test.ts +++ b/public/app/features/plugins/utils.test.ts @@ -4,7 +4,6 @@ import { HOME_NAV_ID } from 'app/core/reducers/navModel'; import { buildPluginSectionNav } from './utils'; describe('buildPluginSectionNav', () => { - const pluginNav = { main: { text: 'Plugin nav' }, node: { text: 'Plugin nav' } }; const app1: NavModelItem = { text: 'App1', id: 'plugin-page-app1', @@ -60,36 +59,36 @@ describe('buildPluginSectionNav', () => { app1.parentItem = appsSection; it('Should return return section nav', () => { - const result = buildPluginSectionNav(appsSection, pluginNav, '/a/plugin1/page1'); + const result = buildPluginSectionNav('/a/plugin1/page1', appsSection); expect(result?.main.text).toBe('apps'); }); it('Should set active page', () => { - const result = buildPluginSectionNav(appsSection, null, '/a/plugin1/page2'); + const result = buildPluginSectionNav('/a/plugin1/page2', appsSection); expect(result?.main.children![0].children![1].active).toBe(true); expect(result?.node.text).toBe('page2'); }); it('Should only set the most specific match as active (not the parents)', () => { - const result = buildPluginSectionNav(appsSection, null, '/a/plugin1/page2'); + const result = buildPluginSectionNav('/a/plugin1/page2', appsSection); expect(result?.main.children![0].children![1].active).toBe(true); expect(result?.main.children![0].active).not.toBe(true); // Parent should not be active }); it('Should set app section to active', () => { - const result = buildPluginSectionNav(appsSection, null, '/a/plugin1'); + const result = buildPluginSectionNav('/a/plugin1', appsSection); expect(result?.main.children![0].active).toBe(true); expect(result?.node.text).toBe('App1'); }); it('Should handle standalone page', () => { - const result = buildPluginSectionNav(adminSection, pluginNav, '/a/app2/config'); + const result = buildPluginSectionNav('/a/app2/config', adminSection); expect(result?.main.text).toBe('Admin'); expect(result?.node.text).toBe('Standalone page'); }); it('Should set nested active page', () => { - const result = buildPluginSectionNav(appsSection, null, '/a/plugin1/page3/page4'); + const result = buildPluginSectionNav('/a/plugin1/page3/page4', appsSection); expect(result?.main.children![0].children![2].children![0].active).toBe(true); expect(result?.node.text).toBe('page4'); }); diff --git a/public/app/features/plugins/utils.ts b/public/app/features/plugins/utils.ts index 075fc3c2542..845785ce876 100644 --- a/public/app/features/plugins/utils.ts +++ b/public/app/features/plugins/utils.ts @@ -30,11 +30,10 @@ export async function loadPlugin(pluginId: string): Promise { return result; } -export function buildPluginSectionNav( - pluginNavSection: NavModelItem, - pluginNav: NavModel | null, - currentUrl: string -): NavModel | undefined { +export function buildPluginSectionNav(currentUrl: string, pluginNavSection?: NavModelItem): NavModel | undefined { + if (!pluginNavSection) { + return undefined; + } // shallow clone as we set active flag const MAX_RECURSION_DEPTH = 10; let copiedPluginNavSection = { ...pluginNavSection }; diff --git a/public/app/routes/RoutesWrapper.tsx b/public/app/routes/RoutesWrapper.tsx new file mode 100644 index 00000000000..19409cd6663 --- /dev/null +++ b/public/app/routes/RoutesWrapper.tsx @@ -0,0 +1,150 @@ +import { css } from '@emotion/css'; +import * as H from 'history'; +import { ComponentType } from 'react'; +import { Router } from 'react-router-dom'; +import { CompatRouter } from 'react-router-dom-v5-compat'; + +import { GrafanaTheme2 } from '@grafana/data/'; +import { HistoryWrapper, locationService, LocationServiceProvider } from '@grafana/runtime'; +import { GlobalStyles, IconButton, ModalRoot, Stack, useSplitter, useStyles2 } from '@grafana/ui'; + +import { AngularRoot } from '../angular/AngularRoot'; +import { AppChrome } from '../core/components/AppChrome/AppChrome'; +import { TOP_BAR_LEVEL_HEIGHT } from '../core/components/AppChrome/types'; +import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList'; +import { ModalsContextProvider } from '../core/context/ModalsContextProvider'; +import { useSidecar } from '../core/context/SidecarContext'; +import AppRootPage from '../features/plugins/components/AppRootPage'; + +type RouterWrapperProps = { + routes?: JSX.Element | false; + bodyRenderHooks: ComponentType[]; + pageBanners: ComponentType[]; +}; +export function RouterWrapper(props: RouterWrapperProps) { + return ( + + + + + + + + + {props.pageBanners.map((Banner, index) => ( + + ))} + {props.routes} + + {props.bodyRenderHooks.map((Hook, index) => ( + + ))} + + + + + + + ); +} + +/** + * Renders both the main app tree and a secondary sidecar app tree to show 2 apps at the same time in a resizable split + * view. + * @param props + * @constructor + */ +export function ExperimentalSplitPaneRouterWrapper(props: RouterWrapperProps) { + const { activePluginId, closeApp } = useSidecar(); + + let { containerProps, primaryProps, secondaryProps, splitterProps } = useSplitter({ + direction: 'row', + initialSize: 0.6, + dragPosition: 'end', + }); + + // The style changes allow the resizing to be more flexible and not constrained by the content dimensions. In the + // future this could be a switch in the useSplitter but for now it's here until this feature is more final. + function alterStyles(props: T): T { + return { + ...props, + style: { ...props.style, overflow: 'auto', minWidth: 'unset', minHeight: 'unset' }, + }; + } + primaryProps = alterStyles(primaryProps); + secondaryProps = alterStyles(secondaryProps); + + // TODO: this should be used to calculate the height of the header but right now results in a error loop when + // navigating to explore "Cannot destructure property 'range' of 'itemState' as it is undefined." + // const headerHeight = useChromeHeaderHeight(); + const styles = useStyles2(getStyles, TOP_BAR_LEVEL_HEIGHT * 2); + const memoryLocationService = new HistoryWrapper(H.createMemoryHistory({ initialEntries: ['/'] })); + + return ( + // Why do we need these 2 wrappers here? We want for one app case to render very similar as if there was no split + // wrapper at all but the split wrapper needs to have these wrappers to attach its container props to. At the same + // time we don't want to rerender the main app when going from 2 apps render to single app render which would happen + // if we removed the wrappers. So the solution is to keep those 2 divs but make them no actually do anything in + // case we are rendering a single app. +
+
+ +
+ {/* Sidecar */} + {activePluginId && ( + <> +
+
+ + + + +
+
+ closeApp(activePluginId)} + /> +
+ +
+
+
+
+
+ + )} +
+ ); +} + +const getStyles = (theme: GrafanaTheme2, headerHeight: number | undefined) => { + return { + secondAppWrapper: css({ + label: 'secondAppWrapper', + display: 'flex', + height: '100%', + paddingTop: headerHeight || 0, + flexGrow: 1, + flexDirection: 'column', + }), + + secondAppToolbar: css({ + label: 'secondAppToolbar', + display: 'flex', + justifyContent: 'flex-end', + }), + + // This is basically the same as grafana-app class. This means the 2 additional wrapper divs that are in between + // grafana-app div and the main app component don't actually change anything in the layout. + dummyWrapper: css({ + label: 'dummyWrapper', + display: 'flex', + height: '100vh', + flexDirection: 'column', + }), + }; +}; From 3aeb8d390e3c2f6f687ea520c3c61676590f7454 Mon Sep 17 00:00:00 2001 From: Claudiu Dragalina-Paraipan Date: Mon, 9 Sep 2024 15:45:59 +0300 Subject: [PATCH 09/18] [authn] update `authlib` and `claims` versions (#93098) * update authlib version to latest * make update-workspace -- 2nd run * manual cleanup of old version --- go.mod | 2 +- go.sum | 4 ++-- go.work.sum | 2 ++ pkg/apimachinery/go.mod | 4 +++- pkg/apimachinery/go.sum | 8 ++++++-- pkg/storage/unified/apistore/go.mod | 2 +- pkg/storage/unified/apistore/go.sum | 4 ++-- pkg/storage/unified/resource/go.mod | 2 +- pkg/storage/unified/resource/go.sum | 4 ++-- 9 files changed, 20 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 7c53c7a2e69..5e4ac9a8ef2 100644 --- a/go.mod +++ b/go.mod @@ -75,7 +75,7 @@ require ( github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41 // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 // @grafana/identity-access-team github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code diff --git a/go.sum b/go.sum index a09176aa7d2..c9132f82318 100644 --- a/go.sum +++ b/go.sum @@ -2259,8 +2259,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41 h1:p+UsX43BoDH5YlG6xUd9xDS3M4sWouy8VJ+ODv5S6uE= github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 h1:e+kFqd2sECBhbxOV1NoVxsudLygNQuu9bO+7FjNTkXo= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= diff --git a/go.work.sum b/go.work.sum index 49ff8ee90b4..62e3d98e882 100644 --- a/go.work.sum +++ b/go.work.sum @@ -547,6 +547,7 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/gomemcache v0.0.0-20240229205252-cd6a66d6fb56/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= @@ -954,6 +955,7 @@ go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index 66ed47e5c30..2c04a4c5df6 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/pkg/apimachinery go 1.23.1 require ( - github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 // @grafana/identity-access-team github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team github.com/stretchr/testify v1.9.0 k8s.io/apimachinery v0.31.0 @@ -32,6 +32,8 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 3e2cde7b9e9..2a54ca811c8 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -28,8 +28,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 h1:e+kFqd2sECBhbxOV1NoVxsudLygNQuu9bO+7FjNTkXo= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -68,6 +68,10 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index cdb401dd379..446b2d333cb 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -43,7 +43,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect - github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e // indirect + github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 9eb389205ce..997b6e84a27 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -161,8 +161,8 @@ github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDP github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 h1:e+kFqd2sECBhbxOV1NoVxsudLygNQuu9bO+7FjNTkXo= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index a8a0a945649..9a867695c41 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -4,7 +4,7 @@ go 1.23.1 require ( github.com/fullstorydev/grpchan v1.1.1 - github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e + github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index de9f59a2b26..2ec9b2b3810 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -128,8 +128,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= -github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 h1:e+kFqd2sECBhbxOV1NoVxsudLygNQuu9bO+7FjNTkXo= +github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e h1:3vNpomyzv714Hgls5vn+fC0vgv8wUOSHepUl7PB5nUs= From 3fde6bd730a7260b4bf1434c8d9785c2b6149098 Mon Sep 17 00:00:00 2001 From: Polidoro-root <54002172+Polidoro-root@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:49:47 -0300 Subject: [PATCH 10/18] I18n: Add constants value tests to avoid typos (#93070) --- .../internationalization/constants.test.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/public/app/core/internationalization/constants.test.ts b/public/app/core/internationalization/constants.test.ts index e0dff39d343..8e50e6f34bf 100644 --- a/public/app/core/internationalization/constants.test.ts +++ b/public/app/core/internationalization/constants.test.ts @@ -1,8 +1,30 @@ import { uniqBy } from 'lodash'; -import { LANGUAGES, VALID_LANGUAGES } from './constants'; +import { + BRAZILIAN_PORTUGUESE, + CHINESE_SIMPLIFIED, + DEFAULT_LANGUAGE, + ENGLISH_US, + FRENCH_FRANCE, + GERMAN_GERMANY, + LANGUAGES, + PSEUDO_LOCALE, + SPANISH_SPAIN, + VALID_LANGUAGES, +} from './constants'; describe('internationalization constants', () => { + it('should have set the constants correctly', () => { + expect(ENGLISH_US).toBe('en-US'); + expect(FRENCH_FRANCE).toBe('fr-FR'); + expect(SPANISH_SPAIN).toBe('es-ES'); + expect(GERMAN_GERMANY).toBe('de-DE'); + expect(BRAZILIAN_PORTUGUESE).toBe('pt-BR'); + expect(CHINESE_SIMPLIFIED).toBe('zh-Hans'); + expect(PSEUDO_LOCALE).toBe('pseudo-LOCALE'); + expect(DEFAULT_LANGUAGE).toBe(ENGLISH_US); + }); + it('should not have duplicate languages codes', () => { const uniqLocales = uniqBy(LANGUAGES, (v) => v.code); expect(LANGUAGES).toHaveLength(uniqLocales.length); From ef486133c2b6f5171f5a95ba2afe7e375683326d Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Mon, 9 Sep 2024 14:01:46 +0100 Subject: [PATCH 11/18] Alerting: Make query wrapper match up datasource UIDs if necessary (#93057) --- .../components/rule-editor/QueryWrapper.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx index 928e111d7b4..5263fea8af6 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx @@ -15,6 +15,7 @@ import { } from '@grafana/data'; import { DataQuery } from '@grafana/schema'; import { GraphThresholdsStyleMode, Icon, InlineField, Input, Tooltip, useStyles2, Stack } from '@grafana/ui'; +import { logInfo } from 'app/features/alerting/unified/Analytics'; import { QueryEditorRow } from 'app/features/query/components/QueryEditorRow'; import { AlertDataQuery, AlertQuery } from 'app/types/unified-alerting-dto'; @@ -82,6 +83,19 @@ export const QueryWrapper = ({ ...cloneDeep(query.model), }; + if (queryWithDefaults.datasource && queryWithDefaults.datasource?.uid !== query.datasourceUid) { + logInfo('rule query datasource and datasourceUid mismatch', { + queryModelDatasourceUid: queryWithDefaults.datasource?.uid || '', + queryDatasourceUid: query.datasourceUid, + datasourceType: query.model.datasource?.type || 'unknown type', + }); + // There are occasions when the rule query model datasource UID and the datasourceUid do not match + // It's unclear as to why this happens, but we need better visibility on why this happens, + // so we log when it does, and make the query model datasource UID match the datasource UID + // We already elsewhere work under the assumption that the datasource settings are fetched from the datasourceUid property + queryWithDefaults.datasource.uid = query.datasourceUid; + } + function SelectingDataSourceTooltip() { const styles = useStyles2(getStyles); return ( From 4ab61cea0ee4d99046df6fcf0a74b362a5f77bcd Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 9 Sep 2024 14:02:09 +0100 Subject: [PATCH 12/18] MSSQL: Add errorsource (#92152) * Add errorsource to mssql * Fix args * Use fmt.Errorf --- pkg/tsdb/mssql/sqleng/sql_engine.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pkg/tsdb/mssql/sqleng/sql_engine.go b/pkg/tsdb/mssql/sqleng/sql_engine.go index f129383b440..cf4ed354181 100644 --- a/pkg/tsdb/mssql/sqleng/sql_engine.go +++ b/pkg/tsdb/mssql/sqleng/sql_engine.go @@ -217,10 +217,13 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG logger.Error("ExecuteQuery panic", "error", r, "stack", string(debug.Stack())) if theErr, ok := r.(error); ok { queryResult.dataResponse.Error = theErr + queryResult.dataResponse.ErrorSource = backend.ErrorSourcePlugin } else if theErrString, ok := r.(string); ok { queryResult.dataResponse.Error = errors.New(theErrString) + queryResult.dataResponse.ErrorSource = backend.ErrorSourcePlugin } else { queryResult.dataResponse.Error = fmt.Errorf("unexpected error - %s", e.userError) + queryResult.dataResponse.ErrorSource = backend.ErrorSourceDownstream } ch <- queryResult } @@ -232,12 +235,13 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG timeRange := query.TimeRange - errAppendDebug := func(frameErr string, err error, query string) { + errAppendDebug := func(frameErr string, err error, query string, source backend.ErrorSource) { var emptyFrame data.Frame emptyFrame.SetMeta(&data.FrameMeta{ ExecutedQueryString: query, }) queryResult.dataResponse.Error = fmt.Errorf("%s: %w", frameErr, err) + queryResult.dataResponse.ErrorSource = source queryResult.dataResponse.Frames = data.Frames{&emptyFrame} ch <- queryResult } @@ -248,13 +252,13 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG // data source specific substitutions interpolatedQuery, err := e.macroEngine.Interpolate(&query, timeRange, interpolatedQuery) if err != nil { - errAppendDebug("interpolation failed", e.TransformQueryError(logger, err), interpolatedQuery) + errAppendDebug("interpolation failed", e.TransformQueryError(logger, err), interpolatedQuery, backend.ErrorSourcePlugin) return } rows, err := e.db.QueryContext(queryContext, interpolatedQuery) if err != nil { - errAppendDebug("db query error", e.TransformQueryError(logger, err), interpolatedQuery) + errAppendDebug("db query error", e.TransformQueryError(logger, err), interpolatedQuery, backend.ErrorSourceDownstream) return } defer func() { @@ -265,7 +269,7 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG qm, err := e.newProcessCfg(query, queryContext, rows, interpolatedQuery) if err != nil { - errAppendDebug("failed to get configurations", err, interpolatedQuery) + errAppendDebug("failed to get configurations", err, interpolatedQuery, backend.ErrorSourcePlugin) return } @@ -273,7 +277,7 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG stringConverters := e.queryResultTransformer.GetConverterList() frame, err := sqlutil.FrameFromRows(rows, e.rowLimit, sqlutil.ToConverters(stringConverters...)...) if err != nil { - errAppendDebug("convert frame from rows error", err, interpolatedQuery) + errAppendDebug("convert frame from rows error", err, interpolatedQuery, backend.ErrorSourcePlugin) return } @@ -295,14 +299,14 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG } if err := convertSQLTimeColumnsToEpochMS(frame, qm); err != nil { - errAppendDebug("converting time columns failed", err, interpolatedQuery) + errAppendDebug("converting time columns failed", err, interpolatedQuery, backend.ErrorSourcePlugin) return } if qm.Format == dataQueryFormatSeries { // time series has to have time column if qm.timeIndex == -1 { - errAppendDebug("db has no time column", errors.New("no time column found"), interpolatedQuery) + errAppendDebug("db has no time column", errors.New("no time column found"), interpolatedQuery, backend.ErrorSourceDownstream) return } @@ -320,7 +324,7 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG var err error if frame, err = convertSQLValueColumnToFloat(frame, i); err != nil { - errAppendDebug("convert value to float failed", err, interpolatedQuery) + errAppendDebug("convert value to float failed", err, interpolatedQuery, backend.ErrorSourcePlugin) return } } @@ -331,7 +335,7 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG originalData := frame frame, err = data.LongToWide(frame, qm.FillMissing) if err != nil { - errAppendDebug("failed to convert long to wide series when converting from dataframe", err, interpolatedQuery) + errAppendDebug("failed to convert long to wide series when converting from dataframe", err, interpolatedQuery, backend.ErrorSourcePlugin) return } From 7e4500c7c9c8d912b4853c4ce6e9b9da55705122 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Mon, 9 Sep 2024 16:03:57 +0300 Subject: [PATCH 13/18] Dashboards: Update tests to be compatible with react router 6 (#93080) * Dashboards: Update tests to be compatible with react router 6 * Update VersionsSettings.test * Update DashboardGrid.test * Update TimeSrv.test * Remove redundant mock * Update PublicDashboardListTable.test * Update PlaylistEditPage.test * Revert TimeSrv changes --- .../settings/DashboardLinksEditView.test.tsx | 11 ---------- .../GeneralSettings.test.tsx | 19 +++------------- .../VersionsSettings.test.tsx | 19 +++------------- .../dashboard/dashgrid/DashboardGrid.test.tsx | 22 +++---------------- .../PublicDashboardListTable.test.tsx | 16 +++----------- .../playlist/PlaylistEditPage.test.tsx | 5 ++--- 6 files changed, 14 insertions(+), 78 deletions(-) diff --git a/public/app/features/dashboard-scene/settings/DashboardLinksEditView.test.tsx b/public/app/features/dashboard-scene/settings/DashboardLinksEditView.test.tsx index a16cdc73ea2..7e25117a5e8 100644 --- a/public/app/features/dashboard-scene/settings/DashboardLinksEditView.test.tsx +++ b/public/app/features/dashboard-scene/settings/DashboardLinksEditView.test.tsx @@ -12,17 +12,6 @@ import { activateFullSceneTree } from '../utils/test-utils'; import { DashboardLinksEditView } from './DashboardLinksEditView'; import { NEW_LINK } from './links/utils'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: jest.fn().mockReturnValue({ - pathname: '/d/dash-1/settings/links', - search: '', - hash: '', - state: null, - key: '5nvxpbdafa', - }), -})); - function render(component: React.ReactNode) { return RTLRender({component}); } diff --git a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx index b4b63faa8fc..edd31d452a5 100644 --- a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx @@ -1,16 +1,12 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { Provider } from 'react-redux'; -import { BrowserRouter } from 'react-router-dom'; import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; -import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { render } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors'; import { BackendSrv, setBackendSrv } from '@grafana/runtime'; -import { GrafanaContext } from 'app/core/context/GrafanaContext'; -import { configureStore } from '../../../../store/configureStore'; import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { GeneralSettingsUnconnected as GeneralSettings, Props } from './GeneralSettings'; @@ -20,7 +16,6 @@ setBackendSrv({ } as unknown as BackendSrv); const setupTestContext = (options: Partial) => { - const store = configureStore(); const defaults: Props = { dashboard: createDashboardModelFixture( { @@ -50,15 +45,7 @@ const setupTestContext = (options: Partial) => { const props = { ...defaults, ...options }; - const { rerender } = render( - - - - - - - - ); + const { rerender } = render(); return { rerender, props }; }; diff --git a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx index 77983e110fb..4b0b91ef6e6 100644 --- a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx @@ -1,13 +1,9 @@ -import { render, screen, waitFor, within } from '@testing-library/react'; +import { screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { Provider } from 'react-redux'; -import { BrowserRouter } from 'react-router-dom'; -import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { render } from 'test/test-utils'; -import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv'; -import { configureStore } from '../../../../store/configureStore'; import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings'; @@ -27,7 +23,6 @@ const queryByFullText = (text: string) => }); function setup() { - const store = configureStore(); const dashboard = createDashboardModelFixture({ id: 74, version: 11, @@ -42,15 +37,7 @@ function setup() { }, }; - return render( - - - - - - - - ); + return render(); } describe('VersionSettings', () => { diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx index be9499a53de..052044af9b4 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx @@ -1,17 +1,12 @@ -import { act, render, screen } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { Router } from 'react-router-dom'; +import { act, screen } from '@testing-library/react'; import { useEffectOnce } from 'react-use'; -import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { render } from 'test/test-utils'; import { TextBoxVariableModel } from '@grafana/data'; -import { locationService } from '@grafana/runtime'; import { Dashboard } from '@grafana/schema'; import appEvents from 'app/core/app_events'; -import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { GetVariables } from 'app/features/variables/state/selectors'; import { VariablesChanged } from 'app/features/variables/types'; -import { configureStore } from 'app/store/configureStore'; import { DashboardMeta } from 'app/types'; import { DashboardModel } from '../state'; @@ -41,18 +36,7 @@ jest.mock('app/features/dashboard/dashgrid/LazyLoader', () => { }); function setup(props: Props) { - const context = getGrafanaContextMock(); - const store = configureStore({}); - - return render( - - - - - - - - ); + return render(); } function getTestDashboard( diff --git a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx index 0e633de2a01..f5d3ad028b0 100644 --- a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx +++ b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx @@ -1,9 +1,7 @@ -import { render, screen, waitForElementToBeRemoved, within } from '@testing-library/react'; +import { screen, waitForElementToBeRemoved, within } from '@testing-library/react'; import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; -import { BrowserRouter } from 'react-router-dom'; -import { TestProvider } from 'test/helpers/TestProvider'; -import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { render } from 'test/test-utils'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { backendSrv } from 'app/core/services/backend_srv'; @@ -69,15 +67,7 @@ afterEach(() => { const selectors = e2eSelectors.pages.PublicDashboards; const renderPublicDashboardTable = async (waitForListRendering?: boolean) => { - const context = getGrafanaContextMock(); - - render( - - - - - - ); + render(); waitForListRendering && (await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')[0], { timeout: 3000 })); }; diff --git a/public/app/features/playlist/PlaylistEditPage.test.tsx b/public/app/features/playlist/PlaylistEditPage.test.tsx index beda508e4f9..37d2f618493 100644 --- a/public/app/features/playlist/PlaylistEditPage.test.tsx +++ b/public/app/features/playlist/PlaylistEditPage.test.tsx @@ -1,14 +1,13 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { History, Location } from 'history'; -import { type match } from 'react-router-dom'; import { TestProvider } from 'test/helpers/TestProvider'; import { locationService } from '@grafana/runtime'; import { RouteDescriptor } from 'app/core/navigation/types'; import { backendSrv } from 'app/core/services/backend_srv'; -import { PlaylistEditPage, RouteParams } from './PlaylistEditPage'; +import { PlaylistEditPage } from './PlaylistEditPage'; import { Playlist } from './types'; jest.mock('@grafana/runtime', () => ({ @@ -27,7 +26,7 @@ async function getTestContext({ name, interval, items, uid }: Partial const playlist = { name, items, interval, uid } as unknown as Playlist; const queryParams = {}; const route = {} as RouteDescriptor; - const match = { params: { uid: 'foo' } } as unknown as match; + const match = { isExact: false, path: '', url: '', params: { uid: 'foo' } }; const location = {} as Location; const history = {} as History; const getMock = jest.spyOn(backendSrv, 'get'); From 2585c58dd42e498883a801e440cfed337b6fe4e9 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Mon, 9 Sep 2024 16:21:05 +0300 Subject: [PATCH 14/18] OptionsUI: Change owners to dashboard squad (#92592) * OptionsUI: Change owners to dashboard squad * Update .github/CODEOWNERS Co-authored-by: Alexa V <239999+axelavargas@users.noreply.github.com> --------- Co-authored-by: Alexa V <239999+axelavargas@users.noreply.github.com> --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 91e8e796005..c3acb119e5d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -404,6 +404,9 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/core/components/TimeSeries/ @grafana/dataviz-squad /public/app/core/components/TimelineChart/ @grafana/dataviz-squad /public/app/core/components/Form/ @grafana/grafana-frontend-platform +/public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad + + /public/app/core/history/ @grafana/explore-squad /public/app/features/admin/ @grafana/identity-access-team From 58907a84649fac3a8b9f78e1648015c5f8b9c866 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 9 Sep 2024 14:25:49 +0100 Subject: [PATCH 15/18] i18n: Add test to check languages match a canonical locale (#93105) add test to check languages match a canonical locale --- public/app/core/internationalization/constants.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/app/core/internationalization/constants.test.ts b/public/app/core/internationalization/constants.test.ts index 8e50e6f34bf..6dee3878b36 100644 --- a/public/app/core/internationalization/constants.test.ts +++ b/public/app/core/internationalization/constants.test.ts @@ -25,6 +25,13 @@ describe('internationalization constants', () => { expect(DEFAULT_LANGUAGE).toBe(ENGLISH_US); }); + it('should match a canonical locale definition', () => { + for (const lang of LANGUAGES) { + const resolved = Intl.getCanonicalLocales(lang.code); + expect(lang.code).toEqual(resolved[0]); + } + }); + it('should not have duplicate languages codes', () => { const uniqLocales = uniqBy(LANGUAGES, (v) => v.code); expect(LANGUAGES).toHaveLength(uniqLocales.length); From c0df15dcde8fe87c99c0ff8d753a006751c241dc Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Mon, 9 Sep 2024 15:35:30 +0200 Subject: [PATCH 16/18] InfluxDB: Fix variable interpolation in influx db (#92988) Fix variable interpolation in influx db --- public/app/plugins/datasource/influxdb/datasource.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index ea1c4e266f7..69fe4452405 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -324,11 +324,6 @@ export default class InfluxDatasource extends DataSourceWithBackend Date: Mon, 9 Sep 2024 14:44:35 +0100 Subject: [PATCH 17/18] Azure: Upgrade grafana-azure-sdk-go (#93009) * Upgrade grafana-azure-sdk-go * Run update-workspace * Update go work * Fix deps * Update go.sum --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5e4ac9a8ef2..b2117a25bd0 100644 --- a/go.mod +++ b/go.mod @@ -85,7 +85,7 @@ require ( github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 // @grafana/sharing-squad github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-aws-sdk v0.30.0 // @grafana/aws-datasources - github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1 // @grafana/partner-datasources + github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.1.0 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group diff --git a/go.sum b/go.sum index c9132f82318..d988a24a782 100644 --- a/go.sum +++ b/go.sum @@ -2283,8 +2283,8 @@ github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e h1:UlEET0InuoFa github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e/go.mod h1:IGRj8oOoxwJbHBYl1+OhS9UjQR0dv6SQOep7HqmtyFU= github.com/grafana/grafana-aws-sdk v0.30.0 h1:6IIetM4s2NbvPOI4/fefsyN84BIb0/T09lHGF/pywo8= github.com/grafana/grafana-aws-sdk v0.30.0/go.mod h1:ZSVPU7IIJSi5lEg+K3Js+EUpZLXxUaBdaQWH+As1ihI= -github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1 h1:90HjoS3kvCd6Thvcl29rtL2+AcSt9AAIDqgQAGk/6T8= -github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1/go.mod h1:yqaupYdH8i42m3VRrmVgNNLGvr4NVjoDmstgZzASAnc= +github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 h1:fV6IgVtViXcYZ4VqTAMuVBTLuGAnI27HhQkaLttzbPE= +github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2/go.mod h1:Cbh94bfL5o6mUSaHFiOkx4r4CRKlo/DJLx4dPL8XrE0= github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 h1:F0O9eTy4jHjEd1Z3/qIza2GdY7PYpTddUeaq9p3NKGU= github.com/grafana/grafana-cloud-migration-snapshot v1.3.0/go.mod h1:bd6Cm06EK0MzRO5ahUpbDz1SxNOKu+fzladbaRPHZPY= github.com/grafana/grafana-google-sdk-go v0.1.0 h1:LKGY8z2DSxKjYfr2flZsWgTRTZ6HGQbTqewE3JvRaNA= From b89f3f811543bdc0a167ab9a5f25d73a3599a6a3 Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Mon, 9 Sep 2024 09:56:43 -0400 Subject: [PATCH 18/18] Ad-Hoc Filters & Scopes: don't remap one-of to regex in frontend (#92995) * send "one-of" and "not-one-of" directly to datasource (instead of changing them to regex) * Added to Ad-hoc and and Scope Filters: The "values" prop ([]string) and the "one-of" and "not-one-"of" operators. "values" is used with one-of and not-one-of. * adds prometheus support for the above --------- Co-authored-by: Ashley Harrison Co-authored-by: Todd Treece --- packages/grafana-data/src/types/scopes.ts | 6 +++- .../grafana-prometheus/src/datasource.test.ts | 4 +++ packages/grafana-prometheus/src/datasource.ts | 29 +++++-------------- pkg/apis/scope/v0alpha1/types.go | 8 +++-- .../scope/v0alpha1/zz_generated.deepcopy.go | 9 +++++- .../scope/v0alpha1/zz_generated.openapi.go | 19 ++++++++++-- ...enerated.openapi_violation_exceptions.list | 1 + pkg/promlib/models/query.go | 8 +++-- pkg/promlib/models/query.panel.schema.json | 14 +++++++++ pkg/promlib/models/query.request.schema.json | 14 +++++++++ pkg/promlib/models/query.types.json | 16 +++++++++- pkg/promlib/models/scope.go | 11 +++++++ pkg/promlib/models/scope_test.go | 13 +++++++-- 13 files changed, 119 insertions(+), 33 deletions(-) diff --git a/packages/grafana-data/src/types/scopes.ts b/packages/grafana-data/src/types/scopes.ts index aac608e3e1d..90579b72017 100644 --- a/packages/grafana-data/src/types/scopes.ts +++ b/packages/grafana-data/src/types/scopes.ts @@ -17,18 +17,22 @@ export interface ScopeDashboardBinding { status: ScopeDashboardBindingStatus; } -export type ScopeFilterOperator = 'equals' | 'not-equals' | 'regex-match' | 'regex-not-match'; +export type ScopeFilterOperator = 'equals' | 'not-equals' | 'regex-match' | 'regex-not-match' | 'one-of' | 'not-one-of'; export const scopeFilterOperatorMap: Record = { '=': 'equals', '!=': 'not-equals', '=~': 'regex-match', '!~': 'regex-not-match', + '=|': 'one-of', + '!=|': 'not-one-of', }; export interface ScopeSpecFilter { key: string; value: string; + // values is used for operators that support multiple values (e.g. one-of, not-one-of) + values?: string[]; operator: ScopeFilterOperator; } diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index 39bf02561ed..664206c9f1f 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -1275,6 +1275,8 @@ describe('modifyQuery', () => { }, { key: 'reg', value: 'regv', operator: '=~' }, { key: 'nreg', value: 'nregv', operator: '!~' }, + { key: 'foo', value: 'bar', operator: '=|' }, + { key: 'bar', value: 'baz', operator: '!=|' }, ]; const expectedScopeFilter: ScopeSpecFilter[] = [ { key: 'eq', value: 'eqv', operator: 'equals' }, @@ -1285,6 +1287,8 @@ describe('modifyQuery', () => { }, { key: 'reg', value: 'regv', operator: 'regex-match' }, { key: 'nreg', value: 'nregv', operator: 'regex-not-match' }, + { key: 'foo', value: 'bar', operator: 'one-of' }, + { key: 'bar', value: 'baz', operator: 'not-one-of' }, ]; const result = ds.generateScopeFilters(adhocFilter); result.forEach((r, i) => { diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 637e701507d..f75aa985b1a 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -603,7 +603,7 @@ export class PrometheusDatasource return this.languageProvider.getLabelKeys().map((k) => ({ value: k, text: k })); } - const labelFilters: QueryBuilderLabelFilter[] = options.filters.map(remapOneOf).map((f) => ({ + const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({ label: f.key, value: f.value, op: f.operator, @@ -620,7 +620,7 @@ export class PrometheusDatasource // By implementing getTagKeys and getTagValues we add ad-hoc filters functionality async getTagValues(options: DataSourceGetTagValuesOptions) { - const labelFilters: QueryBuilderLabelFilter[] = options.filters.map(remapOneOf).map((f) => ({ + const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({ label: f.key, value: f.value, op: f.operator, @@ -822,10 +822,11 @@ export class PrometheusDatasource return []; } - return filters.map(remapOneOf).map((f) => ({ - ...f, - value: this.templateSrv.replace(f.value, {}, this.interpolateQueryExpr), + return filters.map((f) => ({ + key: f.key, operator: scopeFilterOperatorMap[f.operator], + value: this.templateSrv.replace(f.value, {}, this.interpolateQueryExpr), + values: f.values?.map((v) => this.templateSrv.replace(v, {}, this.interpolateQueryExpr)), })); } @@ -834,7 +835,7 @@ export class PrometheusDatasource return expr; } - const finalQuery = filters.map(remapOneOf).reduce((acc, filter) => { + const finalQuery = filters.reduce((acc, filter) => { const { key, operator } = filter; let { value } = filter; if (operator === '=~' || operator === '!~') { @@ -1001,19 +1002,3 @@ export function prometheusRegularEscape(value: T) { export function prometheusSpecialRegexEscape(value: T) { return typeof value === 'string' ? value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]\'+?.()|]/g, '\\\\$&') : value; } - -export function remapOneOf(filter: AdHocVariableFilter) { - let { operator, value, values } = filter; - if (operator === '=|') { - operator = '=~'; - value = values?.map(prometheusRegularEscape).join('|') ?? ''; - } else if (operator === '!=|') { - operator = '!~'; - value = values?.map(prometheusRegularEscape).join('|') ?? ''; - } - return { - ...filter, - operator, - value, - }; -} diff --git a/pkg/apis/scope/v0alpha1/types.go b/pkg/apis/scope/v0alpha1/types.go index 3ba545134ba..c7ed0657ab5 100644 --- a/pkg/apis/scope/v0alpha1/types.go +++ b/pkg/apis/scope/v0alpha1/types.go @@ -26,8 +26,10 @@ type ScopeSpec struct { } type ScopeFilter struct { - Key string `json:"key"` - Value string `json:"value"` + Key string `json:"key"` + Value string `json:"value"` + // Values is used for operators that require multiple values (e.g. one-of and not-one-of). + Values []string `json:"values,omitempty"` Operator FilterOperator `json:"operator"` } @@ -41,6 +43,8 @@ const ( FilterOperatorNotEquals FilterOperator = "not-equals" FilterOperatorRegexMatch FilterOperator = "regex-match" FilterOperatorRegexNotMatch FilterOperator = "regex-not-match" + FilterOperatorOneOf FilterOperator = "one-of" + FilterOperatorNotOneOf FilterOperator = "not-one-of" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/scope/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/scope/v0alpha1/zz_generated.deepcopy.go index 197e04ca196..73a9723640e 100644 --- a/pkg/apis/scope/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/scope/v0alpha1/zz_generated.deepcopy.go @@ -219,6 +219,11 @@ func (in *ScopeDashboardBindingStatus) DeepCopy() *ScopeDashboardBindingStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ScopeFilter) DeepCopyInto(out *ScopeFilter) { *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } return } @@ -347,7 +352,9 @@ func (in *ScopeSpec) DeepCopyInto(out *ScopeSpec) { if in.Filters != nil { in, out := &in.Filters, &out.Filters *out = make([]ScopeFilter, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } diff --git a/pkg/apis/scope/v0alpha1/zz_generated.openapi.go b/pkg/apis/scope/v0alpha1/zz_generated.openapi.go index b5c97dba863..4018d2cf024 100644 --- a/pkg/apis/scope/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/scope/v0alpha1/zz_generated.openapi.go @@ -387,13 +387,28 @@ func schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref common.ReferenceCallback) co Format: "", }, }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, "operator": { SchemaProps: spec.SchemaProps{ - Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`", + Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"not-one-of\"`\n - `\"one-of\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`", Default: "", Type: []string{"string"}, Format: "", - Enum: []interface{}{"equals", "not-equals", "regex-match", "regex-not-match"}, + Enum: []interface{}{"equals", "not-equals", "not-one-of", "one-of", "regex-match", "regex-not-match"}, }, }, }, diff --git a/pkg/apis/scope/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/scope/v0alpha1/zz_generated.openapi_violation_exceptions.list index c2dc43389ae..b473e1d66f0 100644 --- a/pkg/apis/scope/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/scope/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,4 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,FindScopeDashboardBindingsResults,Items API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeDashboardBindingStatus,Groups +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeFilter,Values API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeNodeSpec,LinkID diff --git a/pkg/promlib/models/query.go b/pkg/promlib/models/query.go index 8acd496a264..002102db219 100644 --- a/pkg/promlib/models/query.go +++ b/pkg/promlib/models/query.go @@ -89,8 +89,10 @@ type ScopeSpec struct { // ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go // to avoid import (temp fix) type ScopeFilter struct { - Key string `json:"key"` - Value string `json:"value"` + Key string `json:"key"` + Value string `json:"value"` + // Values is used for operators that require multiple values (e.g. one-of and not-one-of). + Values []string `json:"values,omitempty"` Operator FilterOperator `json:"operator"` } @@ -103,6 +105,8 @@ const ( FilterOperatorNotEquals FilterOperator = "not-equals" FilterOperatorRegexMatch FilterOperator = "regex-match" FilterOperatorRegexNotMatch FilterOperator = "regex-not-match" + FilterOperatorOneOf FilterOperator = "one-of" + FilterOperatorNotOneOf FilterOperator = "not-one-of" ) // Internal interval and range variables diff --git a/pkg/promlib/models/query.panel.schema.json b/pkg/promlib/models/query.panel.schema.json index 0d3ca2c5d2e..778b233805d 100644 --- a/pkg/promlib/models/query.panel.schema.json +++ b/pkg/promlib/models/query.panel.schema.json @@ -34,6 +34,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -213,6 +220,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false diff --git a/pkg/promlib/models/query.request.schema.json b/pkg/promlib/models/query.request.schema.json index 27bbd8310d0..36ff78a2508 100644 --- a/pkg/promlib/models/query.request.schema.json +++ b/pkg/promlib/models/query.request.schema.json @@ -44,6 +44,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -223,6 +230,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false diff --git a/pkg/promlib/models/query.types.json b/pkg/promlib/models/query.types.json index 1d6a5b71fbf..052d0998eac 100644 --- a/pkg/promlib/models/query.types.json +++ b/pkg/promlib/models/query.types.json @@ -8,7 +8,7 @@ { "metadata": { "name": "default", - "resourceVersion": "1715871691891", + "resourceVersion": "1725885733879", "creationTimestamp": "2024-03-25T13:19:04Z" }, "spec": { @@ -31,6 +31,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ @@ -117,6 +124,13 @@ }, "value": { "type": "string" + }, + "values": { + "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ diff --git a/pkg/promlib/models/scope.go b/pkg/promlib/models/scope.go index b2e3d0aa387..ec6c8f95b28 100644 --- a/pkg/promlib/models/scope.go +++ b/pkg/promlib/models/scope.go @@ -2,11 +2,13 @@ package models import ( "fmt" + "strings" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" ) +// ApplyFiltersAndGroupBy takes a raw promQL expression, converts the filters into PromQL matchers, and applies these matchers to the parsed expression. It also applies the group by clause to any aggregate expressions in the parsed expression. func ApplyFiltersAndGroupBy(rawExpr string, scopeFilters, adHocFilters []ScopeFilter, groupBy []string) (string, error) { expr, err := parser.ParseExpr(rawExpr) if err != nil { @@ -98,8 +100,17 @@ func filterToMatcher(f ScopeFilter) (*labels.Matcher, error) { mt = labels.MatchRegexp case FilterOperatorRegexNotMatch: mt = labels.MatchNotRegexp + case FilterOperatorOneOf: + mt = labels.MatchRegexp + case FilterOperatorNotOneOf: + mt = labels.MatchNotRegexp default: return nil, fmt.Errorf("unknown operator %q", f.Operator) } + if f.Operator == FilterOperatorOneOf || f.Operator == FilterOperatorNotOneOf { + if len(f.Values) > 0 { + return labels.NewMatcher(mt, f.Key, strings.Join(f.Values, "|")) + } + } return labels.NewMatcher(mt, f.Key, f.Value) } diff --git a/pkg/promlib/models/scope_test.go b/pkg/promlib/models/scope_test.go index b2c48870405..33ef2653416 100644 --- a/pkg/promlib/models/scope_test.go +++ b/pkg/promlib/models/scope_test.go @@ -68,7 +68,7 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { expectErr: false, }, { - name: "Adhoc and Scope filter conflict - adhoc wins", + name: "Adhoc and Scope filter conflict - adhoc wins (if not oneOf or notOneOf)", query: `http_requests_total{job="prometheus"}`, scopeFilters: []ScopeFilter{ {Key: "status", Value: "404", Operator: FilterOperatorEquals}, @@ -88,6 +88,15 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { expected: `capacity_bytes{job="alloy"} + available_bytes{job="alloy"} / 1024`, expectErr: false, }, + { + name: "OneOf Operator is combined into a single regex filter", + query: `http_requests_total{job="prometheus"}`, + scopeFilters: []ScopeFilter{ + {Key: "status", Values: []string{"404", "400"}, Operator: FilterOperatorOneOf}, + }, + expected: `http_requests_total{job="prometheus",status=~"404|400"}`, + expectErr: false, + }, } for _, tt := range tests { @@ -98,7 +107,7 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { require.Error(t, err) } else { require.NoError(t, err) - require.Equal(t, expr, tt.expected) + require.Equal(t, tt.expected, expr) } }) }