From 64e939fd5dd6042e7ddb22d3e3e83ad68f3fc729 Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Fri, 21 Mar 2025 14:20:27 -0600 Subject: [PATCH 01/86] Unified Storage: Adds span and debug log for paginated List calls (#101912) Adds span and debug log for paginated List calls --- pkg/storage/unified/sql/backend.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/storage/unified/sql/backend.go b/pkg/storage/unified/sql/backend.go index d8f8a604051..d675998f97a 100644 --- a/pkg/storage/unified/sql/backend.go +++ b/pkg/storage/unified/sql/backend.go @@ -650,6 +650,9 @@ func (b *backend) listLatest(ctx context.Context, req *resource.ListRequest, cb // listAtRevision fetches the resources from the resource_history table at a specific revision. func (b *backend) listAtRevision(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) { + ctx, span := b.tracer.Start(ctx, tracePrefix+"listAtRevision") + defer span.End() + // Get the RV iter := &listIter{listRV: req.ResourceVersion, sortAsc: false} if req.NextPageToken != "" { @@ -668,6 +671,10 @@ func (b *backend) listAtRevision(ctx context.Context, req *resource.ListRequest, return 0, apierrors.NewBadRequest("expecting an explicit resource version query") } + // The query below has the potential to be EXTREMELY slow if the resource_history table is big. May be helpful to know + // which stack is calling this. + b.log.Debug("listAtRevision", "ns", req.Options.Key.Namespace, "group", req.Options.Key.Group, "resource", req.Options.Key.Resource, "rv", iter.listRV) + err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error { limit := int64(0) // ignore limit if iter.offset > 0 { From 4cb9042e4cca67e43d255b8bc1c55ac9a4c1b512 Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Fri, 21 Mar 2025 14:30:05 -0600 Subject: [PATCH 02/86] Unified Storage: Update docs for search and load tests (#102648) * add index on dashboard_tag table for dashboard_uid column * updates US docs for search and load tests * Update pkg/storage/unified/README.md Co-authored-by: Will Assis <35489495+gassiss@users.noreply.github.com> --------- Co-authored-by: Will Assis <35489495+gassiss@users.noreply.github.com> --- pkg/storage/unified/README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pkg/storage/unified/README.md b/pkg/storage/unified/README.md index f08da73b33a..8efde1c2c91 100644 --- a/pkg/storage/unified/README.md +++ b/pkg/storage/unified/README.md @@ -253,23 +253,24 @@ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - make changes in `.proto` file - to compile all protobuf files in the repository run `make protobuf` at its top level -## Setting up search (EXPERIMENTAL) -Unified storage now exposes an **experimental** search API. It can be used to search for specific resources, or to filter/query resources. +## Setting up search To enable it, add the following to your `custom.ini` under the `[feature_toggles]` section: ```ini [feature_toggles] +; Used by the Grafana instance +unifiedStorageSearchUI = true +kubernetesClientDashboardsFolders = true + +; Used by unified storage unifiedStorageSearch = true +; (optional) Allows you to sort dashboards by usage insights fields when using enterprise +; unifiedStorageSearchSprinkles = true +; (optional) Will skip search results filter based on user permissions +; unifiedStorageSearchPermissionFiltering = false ``` -To access the api through Grafana, go to Explore -> Query Type -> Search. - -The query needs to be a valid [Bleve query string](https://blevesearch.com/docs/Query-String-Query/). +The dashboard search page has been set up to search unified storage. Additionally, all legacy search calls (e.g. `/api/search`) will go to +unified storage when the dual writer mode is set to 3 or greater. When <= 2, the legacy search api calls will go to legacy storage. -Some example queries are: -- `*` - returns all objects -- `Kind:Playlist` - returns all playlists -- `Spec.inveral:5m` - returns all objects with the spec.inverval field set to 5m -- `+Kind:Playlist +Spec.title:p4` - returns all playlists with the title matching "p4" -- `*foo*` - returns all objects containing "foo" in any field -- `CreatedAt:>="2024-10-17"` - returns all objects created after 2024-10-17 -- `+CreatedAt:>="2024-10-17" +Kind:Playlist` - returns all playlists created after 2024-10-17 +## Running load tests +Load tests and instructions can be found [here](https://github.com/grafana/grafana-api-tests/tree/main/simulation/src/unified_storage). From 62e3c95e7b7d70b8ee02b74b2f4002906d3fe121 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Fri, 21 Mar 2025 14:54:23 -0600 Subject: [PATCH 03/86] K8s: Dashboard history: Fix version endpoint in mode3+ (#102649) --- .../dashboardversion/dashverimpl/dashver.go | 50 ++++++++++++------- .../dashverimpl/dashver_test.go | 42 ++++++++-------- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/pkg/services/dashboardversion/dashverimpl/dashver.go b/pkg/services/dashboardversion/dashverimpl/dashver.go index 03b9b9ee2d9..c054154a72d 100644 --- a/pkg/services/dashboardversion/dashverimpl/dashver.go +++ b/pkg/services/dashboardversion/dashverimpl/dashver.go @@ -221,25 +221,42 @@ func (s *Service) getDashIDMaybeEmpty(ctx context.Context, uid string, orgID int return result.ID, nil } -func (s *Service) getHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, rv int64) (*dashver.DashboardVersionDTO, error) { - out, err := s.k8sclient.Get(ctx, dashboardUID, orgID, v1.GetOptions{ResourceVersion: strconv.FormatInt(rv, 10)}) - if err != nil { - if apierrors.IsNotFound(err) { +func (s *Service) getHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, version int64) (*dashver.DashboardVersionDTO, error) { + // this is an unideal implementation - we have to list all versions and filter here, since there currently is no way to query for the + // generation id in unified storage, so we cannot query for the dashboard version directly, and we cannot use search as history is not indexed. + // use batches to make sure we don't load too much data at once. + const batchSize = 50 + labelSelector := utils.LabelKeyGetHistory + "=" + dashboardUID + var continueToken string + for { + out, err := s.k8sclient.List(ctx, orgID, v1.ListOptions{ + LabelSelector: labelSelector, + Limit: int64(batchSize), + Continue: continueToken, + }) + if err != nil { + if apierrors.IsNotFound(err) { + return nil, dashboards.ErrDashboardNotFound + } + return nil, err + } + if out == nil { return nil, dashboards.ErrDashboardNotFound } - return nil, err - } - if out == nil { - return nil, dashboards.ErrDashboardNotFound - } + for _, item := range out.Items { + if item.GetGeneration() == version { + return s.UnstructuredToLegacyDashboardVersion(ctx, &item, orgID) + } + } - dash, err := s.UnstructuredToLegacyDashboardVersion(ctx, out, orgID) - if err != nil { - return nil, err + continueToken = out.GetContinue() + if continueToken == "" || len(out.Items) == 0 { + break + } } - return dash, nil + return nil, dashboards.ErrDashboardNotFound } func (s *Service) listHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, limit int64, continueToken string) (*dashver.DashboardVersionResponse, error) { @@ -313,18 +330,13 @@ func (s *Service) UnstructuredToLegacyDashboardVersion(ctx context.Context, item created = *updated } - id, err := obj.GetResourceVersionInt64() - if err != nil { - return nil, err - } - restoreVer, err := getRestoreVersion(obj.GetMessage()) if err != nil { return nil, err } out := dashver.DashboardVersionDTO{ - ID: id, + ID: dashVersion, DashboardID: obj.GetDeprecatedInternalID(), // nolint:staticcheck DashboardUID: uid, Created: created, diff --git a/pkg/services/dashboardversion/dashverimpl/dashver_test.go b/pkg/services/dashboardversion/dashverimpl/dashver_test.go index 1e34b144d30..4f210ea906f 100644 --- a/pkg/services/dashboardversion/dashverimpl/dashver_test.go +++ b/pkg/services/dashboardversion/dashverimpl/dashver_test.go @@ -74,7 +74,8 @@ func TestDashboardVersionService(t *testing.T) { require.NoError(t, err) obj.SetUpdatedTimestamp(&updatedTimestamp) mockCli.On("GetUserFromMeta", mock.Anything, "user:1").Return(&user.User{ID: 1}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "10"}, mock.Anything).Return(dash, nil).Once() + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(&unstructured.UnstructuredList{ + Items: []unstructured.Unstructured{*dash}}, nil).Once() res, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, OrgID: 1, @@ -82,7 +83,7 @@ func TestDashboardVersionService(t *testing.T) { }) require.Nil(t, err) require.Equal(t, res, &dashver.DashboardVersionDTO{ - ID: 12, // RV should be used + ID: 10, Version: 10, ParentVersion: 9, DashboardID: 42, @@ -93,22 +94,23 @@ func TestDashboardVersionService(t *testing.T) { }) mockCli.On("GetUserFromMeta", mock.Anything, "user:2").Return(&user.User{ID: 2}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "11"}, mock.Anything).Return(&unstructured.Unstructured{ - Object: map[string]any{ - "metadata": map[string]any{ - "name": "uid", - "resourceVersion": "11", - "generation": int64(11), - "labels": map[string]any{ - utils.LabelKeyDeprecatedInternalID: "42", // nolint:staticcheck + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(&unstructured.UnstructuredList{ + Items: []unstructured.Unstructured{{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "uid", + "resourceVersion": "11", + "generation": int64(11), + "labels": map[string]any{ + utils.LabelKeyDeprecatedInternalID: "42", // nolint:staticcheck + }, + "annotations": map[string]any{ + utils.AnnoKeyCreatedBy: "user:1", + utils.AnnoKeyUpdatedBy: "user:2", // if updated by is set, that is the version creator + }, }, - "annotations": map[string]any{ - utils.AnnoKeyCreatedBy: "user:1", - utils.AnnoKeyUpdatedBy: "user:2", // if updated by is set, that is the version creator - }, - }, - "spec": map[string]any{}, - }}, nil).Once() + "spec": map[string]any{}, + }}}}, nil).Once() res, err = dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, OrgID: 1, @@ -116,7 +118,7 @@ func TestDashboardVersionService(t *testing.T) { }) require.Nil(t, err) require.Equal(t, res, &dashver.DashboardVersionDTO{ - ID: 11, // RV should be used + ID: 11, Version: 11, ParentVersion: 10, DashboardID: 42, @@ -133,7 +135,7 @@ func TestDashboardVersionService(t *testing.T) { dashboardVersionService.k8sclient = mockCli dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders) dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "10"}, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "dashboards.dashboard.grafana.app", Resource: "dashboard"}, "uid")) + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "dashboards.dashboard.grafana.app", Resource: "dashboard"}, "uid")) _, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, @@ -285,7 +287,7 @@ func TestListDashboardVersions(t *testing.T) { require.Equal(t, 1, len(res.Versions)) require.EqualValues(t, &dashver.DashboardVersionResponse{ Versions: []*dashver.DashboardVersionDTO{{ - ID: 12, // should take rv + ID: 5, DashboardID: 42, ParentVersion: 4, Version: 5, // should take from spec From 8becf33d3104228d57231954e6fb052e142afb2c Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:01:10 -0600 Subject: [PATCH 04/86] Chore: Simplify `releasefinder.sh` logic (#102569) * baldm0mma/ simplify * baldm0mma/ add pr info * baldm0mma/ update logic * baldm0mma/ update language * baldm0mma/ add PR title * baldm0mma/ simplify logic * baldm0mma/ add context * baldm0mma/ adjust logic --- scripts/releasefinder.sh | 86 +++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/scripts/releasefinder.sh b/scripts/releasefinder.sh index 06b287f691e..adc0b7809dd 100755 --- a/scripts/releasefinder.sh +++ b/scripts/releasefinder.sh @@ -2,10 +2,9 @@ # This script finds which Grafana releases include a specific commit. # It checks both release branches and tags to determine: -# 1. Which release branches contain the commit -# 2. The first release tag that included the commit -# 3. Which release tags include the commit -# 4. The first release that included the commit +# 1. Which previous releases include the commit +# 2. Which upcoming releases will include the commit +# 3. The first release that included the commit # # Usage: ./scripts/releasefinder.sh # The commit hash can be either: @@ -59,18 +58,14 @@ echo echo "Commit details:" echo " Author: $(git log -1 --format="%an <%ae>" "$COMMIT_HASH")" echo " Date: $(git log -1 --format="%ad" --date=iso "$COMMIT_HASH")" -echo -# Check for backport information -echo "Backport information:" -if git log -1 --pretty=format:"%B" "$COMMIT_HASH" | grep -q "cherry picked from commit"; then - ORIGINAL_COMMIT=$(git log -1 --pretty=format:"%B" "$COMMIT_HASH" | grep "cherry picked from commit" | sed 's/.*cherry picked from commit \([a-f0-9]*\).*/\1/') - echo " This is a backport from commit: $ORIGINAL_COMMIT" - echo " Original commit details:" - echo " Author: $(git log -1 --format="%an <%ae>" "$ORIGINAL_COMMIT")" - echo " Date: $(git log -1 --format="%ad" --date=iso "$ORIGINAL_COMMIT")" -else - echo " Not a backport" +# Extract original PR number and create link +PR_NUMBER=$(git log -1 --pretty=format:"%B" "$COMMIT_HASH" | grep -o '#[0-9]\+' | head -n1 | tr -d '#') +if [ ! -z "$PR_NUMBER" ]; then + # Extract PR title (first line of commit message) + PR_TITLE=$(git log -1 --pretty=format:"%s" "$COMMIT_HASH") + echo " PR: #$PR_NUMBER - $PR_TITLE" + echo " Link: https://github.com/grafana/grafana/pull/$PR_NUMBER" fi echo @@ -105,46 +100,39 @@ for tag in $(git tag | sort -V); do fi done -# Print release branches -echo "Included in release branches:" +# Print previous releases if they exist +if [ ${#direct_tags[@]} -gt 0 ] || [ ${#included_tags[@]} -gt 0 ]; then + echo "This commit has been included in these PREVIOUS on-prem releases:" + # Get all tags sorted + all_tags=($(printf "%s\n" "${direct_tags[@]}" "${included_tags[@]}" | sort -V)) + # Get the first release + first_release="${all_tags[0]}" + # Print all tags with annotation for the first release + for tag in "${all_tags[@]}"; do + if [ "$tag" = "$first_release" ]; then + echo " - $tag (first release)" + else + echo " - $tag" + fi + done + echo + echo "Note: This code may have been backported to previous release branches. Please check the original PR for backport information." + echo +fi + +# Print upcoming releases if [ ${#release_branches[@]} -eq 0 ]; then - echo " None" + echo " This commit is not yet included in any release branches." + echo " The corresponding release branch has likely not been created yet." else + echo "This commit will be included in these UPCOMING on-prem releases:" for branch in "${release_branches[@]}"; do # Convert branch name to tag format (e.g., release-11.5.0 -> v11.5.0) tag_version="v${branch#release-}" - # Check if tag exists - if git tag | grep -q "^$tag_version$"; then - echo " - $branch" - else - echo " - $branch (release for this branch upcoming)" + # Only show branches that don't have a corresponding tag yet + if ! git tag | grep -q "^$tag_version$"; then + echo " - $tag_version" fi done | sort -V fi echo - -# Print initial release tag -echo "Initial release tag (the first release in which this commit was included):" -if [ ${#direct_tags[@]} -eq 0 ]; then - echo " None" -else - printf " - %s\n" "${direct_tags[@]}" | sort -V -fi -echo - -# Print included tags -echo "Included in these release tags (the subsequent releases that included this commit):" -if [ ${#included_tags[@]} -eq 0 ]; then - echo " None" -else - printf " - %s\n" "${included_tags[@]}" | sort -V -fi -echo - -# Find first release -if [ ${#direct_tags[@]} -gt 0 ]; then - first_release=$(printf "%s\n" "${direct_tags[@]}" | sort -V | head -n1) - echo "First included in release: $first_release" -else - echo "Not included in any releases" -fi From 1b1f626eddd5fc846c54212c2b92d88a05c4518c Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Fri, 21 Mar 2025 18:47:00 -0400 Subject: [PATCH 05/86] SQL Expressions: Fix error handling (#102650) --- pkg/expr/sql/parser.go | 2 +- pkg/expr/sql_command.go | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pkg/expr/sql/parser.go b/pkg/expr/sql/parser.go index 12269b4a68f..c65e4b89131 100644 --- a/pkg/expr/sql/parser.go +++ b/pkg/expr/sql/parser.go @@ -14,7 +14,7 @@ var logger = log.New("sql_expr") func TablesList(rawSQL string) ([]string, error) { stmt, err := sqlparser.Parse(rawSQL) if err != nil { - logger.Error("error parsing sql: %s", err.Error(), "sql", rawSQL) + logger.Error("error parsing sql", "error", err.Error(), "sql", rawSQL) return nil, fmt.Errorf("error parsing sql: %s", err.Error()) } diff --git a/pkg/expr/sql_command.go b/pkg/expr/sql_command.go index 0b4d7ab698e..8bcde6abb9b 100644 --- a/pkg/expr/sql_command.go +++ b/pkg/expr/sql_command.go @@ -14,6 +14,16 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" ) +var ( + ErrMissingSQLQuery = errutil.BadRequest("sql-missing-query").Errorf("missing SQL query") + ErrInvalidSQLQuery = errutil.BadRequest("sql-invalid-sql").MustTemplate( + "invalid SQL query: {{ .Private.query }} err: {{ .Error }}", + errutil.WithPublic( + "Invalid SQL query: {{ .Public.error }}", + ), + ) +) + // SQLCommand is an expression to run SQL over results type SQLCommand struct { query string @@ -25,15 +35,20 @@ type SQLCommand struct { // NewSQLCommand creates a new SQLCommand. func NewSQLCommand(refID, rawSQL string, limit int64) (*SQLCommand, error) { if rawSQL == "" { - return nil, errutil.BadRequest("sql-missing-query", - errutil.WithPublicMessage("missing SQL query")) + return nil, ErrMissingSQLQuery } tables, err := sql.TablesList(rawSQL) if err != nil { logger.Warn("invalid sql query", "sql", rawSQL, "error", err) - return nil, errutil.BadRequest("sql-invalid-sql", - errutil.WithPublicMessage(fmt.Sprintf("invalid SQL query: %s", err)), - ) + return nil, ErrInvalidSQLQuery.Build(errutil.TemplateData{ + Error: err, + Public: map[string]any{ + "error": err.Error(), + }, + Private: map[string]any{ + "query": rawSQL, + }, + }) } if len(tables) == 0 { logger.Warn("no tables found in SQL query", "sql", rawSQL) From 606d30400f5ad9b288bacda29e62051f15936f38 Mon Sep 17 00:00:00 2001 From: drew08t Date: Fri, 21 Mar 2025 15:55:37 -0700 Subject: [PATCH 06/86] Remove empty file --- packages/grafana-ui/src/components/Table/TableRT/TableRT.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 packages/grafana-ui/src/components/Table/TableRT/TableRT.tsx diff --git a/packages/grafana-ui/src/components/Table/TableRT/TableRT.tsx b/packages/grafana-ui/src/components/Table/TableRT/TableRT.tsx deleted file mode 100644 index e69de29bb2d..00000000000 From 010d13634cd6877abd27f1b29895659ada1e3c78 Mon Sep 17 00:00:00 2001 From: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:03:41 -0700 Subject: [PATCH 07/86] TableNG: Update util tests (#102646) --- .../components/Table/TableNG/utils.test.ts | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts index 64cf196fa9b..31612c1bbd0 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts @@ -19,6 +19,9 @@ import { TableCellHeight, } from '@grafana/schema'; +import { Trans } from '../../../utils/i18n'; +import { PanelContext } from '../../PanelChrome'; + import { mapFrameToDataGrid, myRowRenderer } from './TableNG'; import { COLUMN, TABLE } from './constants'; import { TableColumn } from './types'; @@ -290,7 +293,9 @@ describe('TableNG utils', () => { // Check that we have two spans with correct content const [countSpan, valueSpan] = divElement.props.children; - expect(countSpan.props.children).toBe('Count'); + expect(countSpan.type).toBe('span'); + expect(countSpan.props.children.type).toBe(Trans); + expect(countSpan.props.children.props.i18nKey).toBe('grafana-ui.table.count'); expect(valueSpan.props.children).toBe('3'); }); }); @@ -1041,7 +1046,8 @@ describe('TableNG utils', () => { 40, // defaultRowHeight 8, // padding false, // textWrap - false // cellInspect + false, // cellInspect + TableCellDisplayMode.Auto // cellType ); expect(result).toBe(true); @@ -1066,7 +1072,8 @@ describe('TableNG utils', () => { 40, // defaultRowHeight 8, // padding false, // textWrap - false // cellInspect + false, // cellInspect + TableCellDisplayMode.Auto // cellType ); expect(result).toBe(false); @@ -1090,7 +1097,8 @@ describe('TableNG utils', () => { 40, // defaultRowHeight 8, // padding true, // textWrap ENABLED - false // cellInspect + false, // cellInspect + TableCellDisplayMode.Auto // cellType ); expect(result).toBe(false); @@ -1114,7 +1122,8 @@ describe('TableNG utils', () => { 40, // defaultRowHeight 8, // padding false, // textWrap - true // cellInspect ENABLED + true, // cellInspect ENABLED + TableCellDisplayMode.Auto // cellType ); expect(result).toBe(false); @@ -1711,11 +1720,38 @@ describe('TableNG utils', () => { } as any; }; + const mockPanelContext = { + id: 1, + title: 'Test Panel', + description: 'Test Description', + width: 800, + height: 600, + timeRange: { from: 'now-6h', to: 'now' }, + timeZone: 'browser', + onTimeRangeChange: jest.fn(), + onOptionsChange: jest.fn(), + onFieldConfigChange: jest.fn(), + onInstanceStateChange: jest.fn(), + replaceVariables: jest.fn(), + eventBus: { + publish: jest.fn(), + subscribe: jest.fn(), + unsubscribe: jest.fn(), + }, + } as unknown as PanelContext; + + const mockData = createDataFrame({ + fields: [ + { name: 'Time', type: FieldType.time, values: [] }, + { name: 'Value', type: FieldType.number, values: [] }, + ], + }); + it('returns null for non-expanded child rows', () => { const props = createMockProps(1, false, 0); const expandedRows: number[] = []; // No expanded rows - const result = myRowRenderer('key-0', props, expandedRows); + const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false); expect(result).toBeNull(); }); @@ -1724,7 +1760,7 @@ describe('TableNG utils', () => { const props = createMockProps(1, false, 0); const expandedRows: number[] = [0]; // Row 0 is expanded - const result = myRowRenderer('key-0', props, expandedRows); + const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false); expect(result).not.toBeNull(); }); @@ -1733,7 +1769,7 @@ describe('TableNG utils', () => { const props = createMockProps(0, true, 0); const expandedRows: number[] = [0]; // Row 0 is expanded - const result = myRowRenderer('key-0', props, expandedRows) as JSX.Element; + const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element; expect(result.props['aria-expanded']).toBe(true); }); @@ -1742,7 +1778,7 @@ describe('TableNG utils', () => { const props = createMockProps(0, true, 0); const expandedRows: number[] = []; // No expanded rows - const result = myRowRenderer('key-0', props, expandedRows) as JSX.Element; + const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element; expect(result.props['aria-expanded']).toBe(false); }); @@ -1751,7 +1787,7 @@ describe('TableNG utils', () => { const props = createMockProps(0, false, 0); const expandedRows: number[] = []; - const result = myRowRenderer('key-0', props, expandedRows) as JSX.Element; + const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element; expect(result.props['aria-expanded']).toBeUndefined(); }); From 4d79d5e80c2cac76414ba6e1c4873fbce6ecc483 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Fri, 21 Mar 2025 21:22:59 -0600 Subject: [PATCH 08/86] K8s: Fix legacy dashboard folder check (#102660) --- pkg/registry/apis/dashboard/legacy_storage.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/registry/apis/dashboard/legacy_storage.go b/pkg/registry/apis/dashboard/legacy_storage.go index 20445e5aa6c..2037376723f 100644 --- a/pkg/registry/apis/dashboard/legacy_storage.go +++ b/pkg/registry/apis/dashboard/legacy_storage.go @@ -39,6 +39,10 @@ func (s *DashboardStorage) NewStore(dash utils.ResourceInfo, scheme *runtime.Sch optsGetter := apistore.NewRESTOptionsGetterForClient(client, defaultOpts.StorageConfig.Config, ) + optsGetter.RegisterOptions(dash.GroupResource(), apistore.StorageOptions{ + EnableFolderSupport: true, + RequireDeprecatedInternalID: true, + }) store, err := grafanaregistry.NewRegistryStore(scheme, dash, optsGetter) return &storeWrapper{ From bb8392c9a1c4380a8380ebb1903d00cfc1b72726 Mon Sep 17 00:00:00 2001 From: Kristina Date: Sat, 22 Mar 2025 14:25:12 -0500 Subject: [PATCH 09/86] Transformations: Correct documentation around prepare time series (#102553) * Correct documentation around prepare time series * Update public/app/features/transformers/docs/content.ts Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> --------- Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> --- .../transform-data/index.md | 48 +++++++++++-------- .../app/features/transformers/docs/content.ts | 47 ++++++++++-------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md index cfc5a5e29e5..c0a4301d6ae 100644 --- a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md +++ b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @@ -1189,18 +1189,13 @@ Use this transformation to address issues when a data source returns time series #### Available options -##### Multi-frame time series - -Use this option to transform the time series data frame from the wide format to the long format. This is particularly helpful when your data source delivers time series information in a format that needs to be reshaped for optimal compatibility with your visualization. +##### Wide time series -**Example: Converting from wide to long format** +Select this option to transform the time series data frame from the long format to the wide format. -| Timestamp | Value1 | Value2 | -| ------------------- | ------ | ------ | -| 2023-01-01 00:00:00 | 10 | 20 | -| 2023-01-01 01:00:00 | 15 | 25 | +A wide time series combines data into a single frame with one shared, ascending time field. Time fields do not repeat and multiple values extend in separate columns. -**Transformed to:** +**Example: Converting from long to wide format** | Timestamp | Variable | Value | | ------------------- | -------- | ----- | @@ -1209,25 +1204,38 @@ Use this option to transform the time series data frame from the wide format to | 2023-01-01 01:00:00 | Value1 | 15 | | 2023-01-01 01:00:00 | Value2 | 25 | -##### Wide time series +**Transformed to:** -Select this option to transform the time series data frame from the long format to the wide format. If your data source returns time series data in a long format and your visualization requires a wide format, this transformation simplifies the process. +| Timestamp | Value1 | Value2 | +| ------------------- | ------ | ------ | +| 2023-01-01 00:00:00 | 10 | 20 | +| 2023-01-01 01:00:00 | 15 | 25 | -**Example: Converting from long to wide format** +##### Multi-frame time series -| Timestamp | Variable | Value | -| ------------------- | -------- | ----- | -| 2023-01-01 00:00:00 | Value1 | 10 | -| 2023-01-01 00:00:00 | Value2 | 20 | -| 2023-01-01 01:00:00 | Value1 | 15 | -| 2023-01-01 01:00:00 | Value2 | 25 | +Multi-frame time series break data into multiple frames that all contain two fields: a time field and a numeric value field. Time is always ascending. String values are represented as field labels. + +##### Long time series + +A long time series combines data to one frame, with the first field being an ascending time field. The time field might have duplicates. String values are in separate fields, and there might be more than one. + +**Example: Converting to long format** + +| Value1 | Value2 | Timestamp | +| ------ | ------ | ------------------- | +| 10 | 20 | 2023-01-03 00:00:00 | +| 30 | 40 | 2023-01-02 00:00:00 | +| 50 | 60 | 2023-01-01 00:00:00 | +| 70 | 80 | 2023-01-01 00:00:00 | **Transformed to:** | Timestamp | Value1 | Value2 | | ------------------- | ------ | ------ | -| 2023-01-01 00:00:00 | 10 | 20 | -| 2023-01-01 01:00:00 | 15 | 25 | +| 2023-01-01 00:00:00 | 70 | 80 | +| 2023-01-01 01:00:00 | 50 | 60 | +| 2023-01-02 01:00:00 | 30 | 40 | +| 2023-01-03 01:00:00 | 10 | 20 | ### Reduce diff --git a/public/app/features/transformers/docs/content.ts b/public/app/features/transformers/docs/content.ts index 10219399ad2..25d0a99ce0b 100644 --- a/public/app/features/transformers/docs/content.ts +++ b/public/app/features/transformers/docs/content.ts @@ -1218,18 +1218,13 @@ Use this transformation to address issues when a data source returns time series #### Available options -##### Multi-frame time series - -Use this option to transform the time series data frame from the wide format to the long format. This is particularly helpful when your data source delivers time series information in a format that needs to be reshaped for optimal compatibility with your visualization. +##### Wide time series -**Example: Converting from wide to long format** +Select this option to transform the time series data frame from the long format to the wide format. If your data source returns time series data in a long format and your visualization requires a wide format, this transformation simplifies the process. -| Timestamp | Value1 | Value2 | -|---------------------|--------|--------| -| 2023-01-01 00:00:00 | 10 | 20 | -| 2023-01-01 01:00:00 | 15 | 25 | +A wide time series combines data into a single frame with one shared, ascending time field. Time fields do not repeat and multiple values extend in separate columns. -**Transformed to:** +**Example: Converting from long to wide format** | Timestamp | Variable | Value | |---------------------|----------|-------| @@ -1238,26 +1233,38 @@ Use this option to transform the time series data frame from the wide format to | 2023-01-01 01:00:00 | Value1 | 15 | | 2023-01-01 01:00:00 | Value2 | 25 | +**Transformed to:** -##### Wide time series +| Timestamp | Value1 | Value2 | +|---------------------|--------|--------| +| 2023-01-01 00:00:00 | 10 | 20 | +| 2023-01-01 01:00:00 | 15 | 25 | -Select this option to transform the time series data frame from the long format to the wide format. If your data source returns time series data in a long format and your visualization requires a wide format, this transformation simplifies the process. +##### Multi-frame time series -**Example: Converting from long to wide format** +Multi-frame time series break data into multiple frames that all contain two fields: a time field and a numeric value field. Time is always ascending. String values are represented as field labels. -| Timestamp | Variable | Value | -|---------------------|----------|-------| -| 2023-01-01 00:00:00 | Value1 | 10 | -| 2023-01-01 00:00:00 | Value2 | 20 | -| 2023-01-01 01:00:00 | Value1 | 15 | -| 2023-01-01 01:00:00 | Value2 | 25 | +##### Long time series + +A long time series combines data into one frame, with the first field being an ascending time field. The time field might have duplicates. String values are in separate fields, and there might be more than one. + +**Example: Converting to long format** + +| Value1 | Value2 | Timestamp | +|--------|--------|---------------------| +| 10 | 20 | 2023-01-03 00:00:00 | +| 30 | 40 | 2023-01-02 00:00:00 | +| 50 | 60 | 2023-01-01 00:00:00 | +| 70 | 80 | 2023-01-01 00:00:00 | **Transformed to:** | Timestamp | Value1 | Value2 | |---------------------|--------|--------| -| 2023-01-01 00:00:00 | 10 | 20 | -| 2023-01-01 01:00:00 | 15 | 25 | +| 2023-01-01 00:00:00 | 70 | 80 | +| 2023-01-01 01:00:00 | 50 | 60 | +| 2023-01-02 01:00:00 | 30 | 40 | +| 2023-01-03 01:00:00 | 10 | 20 | `; }, From c661077651ee689976803223ecd082c355432046 Mon Sep 17 00:00:00 2001 From: jackyin <648588267@qq.com> Date: Sun, 23 Mar 2025 03:44:48 +0800 Subject: [PATCH 10/86] Bar Chart: Crash when no number field exist (#102265) * fix bar chart crash * format * use optional chaining --- public/app/plugins/panel/barchart/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index 9b25e678f1f..79d9629b11b 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -247,7 +247,7 @@ export const prepConfig = ({ series, totalSeries, color, orientation, options, t // use opacity from first numeric field let opacityField = frame.fields.find((f) => f.type === FieldType.number)!; - fillOpacity = (opacityField.config.custom.fillOpacity ?? 100) / 100; + fillOpacity = (opacityField?.config?.custom?.fillOpacity ?? 100) / 100; getColor = (seriesIdx: number, valueIdx: number) => { let field = frame.fields[seriesIdx]; From 543c0bbccbc72d88e7eed3a54614d2fe1759126e Mon Sep 17 00:00:00 2001 From: Marco de Abreu Date: Sat, 22 Mar 2025 23:47:27 +0100 Subject: [PATCH 11/86] App platform: Add cleanup job for dashboards when going through /apis (kubectl) (#102506) * Add dashboard cleanup job Change log message Adjust logic to account for new head RV logic Don't update lastResourceVersion due to pagination Save improvements * Address review feedback * Update docs. * Remove docs * Rename config --------- Co-authored-by: Marco de Abreu <18629099+marcoabreu@users.noreply.github.com> --- pkg/api/dashboard_test.go | 4 + pkg/api/folder_bench_test.go | 4 + .../backgroundsvcs/background_services.go | 3 + .../accesscontrol/accesscontrol_test.go | 6 +- .../annotationsimpl/annotations_test.go | 11 +- pkg/services/dashboards/dashboard.go | 1 + .../dashboards/dashboard_service_mock.go | 18 + .../dashboards/service/dashboard_service.go | 299 ++++++++++++++- .../dashboard_service_integration_test.go | 10 + .../service/dashboard_service_test.go | 345 +++++++++++++++++- .../service/service_test.go | 5 + pkg/services/folder/folderimpl/folder_test.go | 19 +- .../libraryelements/libraryelements_test.go | 8 + .../librarypanels/librarypanels_test.go | 8 +- pkg/services/ngalert/testutil/testutil.go | 4 + .../publicdashboards/api/query_test.go | 5 + .../publicdashboards/service/service_test.go | 6 +- pkg/services/quota/quotaimpl/quota_test.go | 6 +- pkg/setting/setting.go | 4 + pkg/setting/setting_k8s_dashboard_cleanup.go | 53 +++ 20 files changed, 788 insertions(+), 31 deletions(-) create mode 100644 pkg/setting/setting_k8s_dashboard_cleanup.go diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 549532806bc..0dfdcd43934 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -23,8 +23,10 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db/dbtest" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -875,6 +877,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr cfg, dashboardStore, folderStore, features, folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(), ) require.NoError(t, err) dashboardService.(dashboards.PermissionsRegistrationService).RegisterDashboardPermissions(dashboardPermissions) @@ -884,6 +887,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr cfg, dashboardStore, folderStore, features, folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(), ) require.NoError(t, err) diff --git a/pkg/api/folder_bench_test.go b/pkg/api/folder_bench_test.go index 5847529c941..03a9ab1cf63 100644 --- a/pkg/api/folder_bench_test.go +++ b/pkg/api/folder_bench_test.go @@ -18,8 +18,10 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -476,6 +478,8 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog sc.cfg, dashStore, folderStore, features, folderPermissions, ac, folderServiceWithFlagOn, fStore, nil, client.MockTestRestConfig{}, nil, quotaSrv, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sc.db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(b, err) diff --git a/pkg/registry/backgroundsvcs/background_services.go b/pkg/registry/backgroundsvcs/background_services.go index b0f339ed29d..621c2eee751 100644 --- a/pkg/registry/backgroundsvcs/background_services.go +++ b/pkg/registry/backgroundsvcs/background_services.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/services/authn/authnimpl" "github.com/grafana/grafana/pkg/services/cleanup" "github.com/grafana/grafana/pkg/services/cloudmigration" + "github.com/grafana/grafana/pkg/services/dashboards/service" "github.com/grafana/grafana/pkg/services/dashboardsnapshots" "github.com/grafana/grafana/pkg/services/grpcserver" "github.com/grafana/grafana/pkg/services/guardian" @@ -69,6 +70,7 @@ func ProvideBackgroundServiceRegistry( zanzanaReconciler *dualwrite.ZanzanaReconciler, appRegistry *appregistry.Service, pluginDashboardUpdater *plugindashboardsservice.DashboardUpdater, + dashboardServiceImpl *service.DashboardServiceImpl, // Need to make sure these are initialized, is there a better place to put them? _ dashboardsnapshots.Service, _ serviceaccounts.Service, _ *guardian.Provider, @@ -115,6 +117,7 @@ func ProvideBackgroundServiceRegistry( zanzanaReconciler, appRegistry, pluginDashboardUpdater, + dashboardServiceImpl, ) } diff --git a/pkg/services/annotations/accesscontrol/accesscontrol_test.go b/pkg/services/annotations/accesscontrol/accesscontrol_test.go index 1bff50c2dd3..c92e1b9546b 100644 --- a/pkg/services/annotations/accesscontrol/accesscontrol_test.go +++ b/pkg/services/annotations/accesscontrol/accesscontrol_test.go @@ -10,6 +10,8 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" @@ -50,7 +52,9 @@ func TestIntegrationAuthorize(t *testing.T) { fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, nil, sql, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService()) dashSvc, err := dashboardsservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), - ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sql, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) dashSvc.RegisterDashboardPermissions(accesscontrolmock.NewMockedPermissionsService()) diff --git a/pkg/services/annotations/annotationsimpl/annotations_test.go b/pkg/services/annotations/annotationsimpl/annotations_test.go index 145c94c0748..8e169e88401 100644 --- a/pkg/services/annotations/annotationsimpl/annotations_test.go +++ b/pkg/services/annotations/annotationsimpl/annotations_test.go @@ -12,7 +12,9 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" @@ -63,7 +65,9 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) { fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, nil, sql, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService()) dashSvc, err := dashboardsservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), accesscontrolmock.NewMockedPermissionsService(), - ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sql, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) dashSvc.RegisterDashboardPermissions(accesscontrolmock.NewMockedPermissionsService()) repo := ProvideService(sql, cfg, features, tagService, tracing.InitializeTracerForTest(), ruleStore, dashSvc) @@ -246,7 +250,10 @@ func TestIntegrationAnnotationListingWithInheritedRBAC(t *testing.T) { fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, nil, sql, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService()) dashSvc, err := dashboardsservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, features, accesscontrolmock.NewMockedPermissionsService(), - ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sql, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), + ) require.NoError(t, err) dashSvc.RegisterDashboardPermissions(accesscontrolmock.NewMockedPermissionsService()) cfg.AnnotationMaximumTagsLength = 60 diff --git a/pkg/services/dashboards/dashboard.go b/pkg/services/dashboards/dashboard.go index 92232c95bd8..b904e3c2e07 100644 --- a/pkg/services/dashboards/dashboard.go +++ b/pkg/services/dashboards/dashboard.go @@ -35,6 +35,7 @@ type DashboardService interface { GetAllDashboardsByOrgId(ctx context.Context, orgID int64) ([]*Dashboard, error) SoftDeleteDashboard(ctx context.Context, orgID int64, dashboardUid string) error RestoreDashboard(ctx context.Context, dashboard *Dashboard, user identity.Requester, optionalFolderUID string) error + CleanUpDashboard(ctx context.Context, dashboardUID string, orgId int64) error CleanUpDeletedDashboards(ctx context.Context) (int64, error) GetSoftDeletedDashboard(ctx context.Context, orgID int64, uid string) (*Dashboard, error) CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error) diff --git a/pkg/services/dashboards/dashboard_service_mock.go b/pkg/services/dashboards/dashboard_service_mock.go index 2e37ea6f3b8..ad526998488 100644 --- a/pkg/services/dashboards/dashboard_service_mock.go +++ b/pkg/services/dashboards/dashboard_service_mock.go @@ -562,6 +562,24 @@ func (_m *FakeDashboardService) SoftDeleteDashboard(ctx context.Context, orgID i return r0 } +// CleanUpDashboard provides a mock function with given fields: ctx, dashboardUID, orgId +func (_m *FakeDashboardService) CleanUpDashboard(ctx context.Context, dashboardUID string, orgId int64) error { + ret := _m.Called(ctx, dashboardUID, orgId) + + if len(ret) == 0 { + panic("no return value specified for CleanUpDashboard") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { + r0 = rf(ctx, dashboardUID, orgId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewFakeDashboardService creates a new instance of FakeDashboardService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewFakeDashboardService(t interface { diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 0a11d43f4d7..d85dd62405b 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -29,9 +29,12 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/utils" folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/slugify" + "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver" @@ -56,6 +59,7 @@ import ( "github.com/grafana/grafana/pkg/storage/unified/search" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util/retryer" + "go.opentelemetry.io/otel/attribute" ) var ( @@ -68,6 +72,11 @@ var ( tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/dashboards/service") ) +const ( + k8sDashboardKvNamespace = "dashboard-cleanup" + k8sDashboardKvLastResourceVersionKey = "last-resource-version" +) + type DashboardServiceImpl struct { cfg *setting.Cfg log log.Logger @@ -82,11 +91,271 @@ type DashboardServiceImpl struct { k8sclient client.K8sHandler metrics *dashboardsMetrics publicDashboardService publicdashboards.ServiceWrapper + serverLockService *serverlock.ServerLockService + kvstore kvstore.KVStore dashboardPermissionsReady chan struct{} } +func (dr *DashboardServiceImpl) startK8sDeletedDashboardsCleanupJob(ctx context.Context) chan struct{} { + done := make(chan struct{}) + go func() { + defer close(done) + + ticker := time.NewTicker(dr.cfg.K8sDashboardCleanup.Interval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if err := dr.executeCleanupWithLock(ctx); err != nil { + dr.log.Error("Failed to execute k8s dashboard cleanup", "error", err) + } + } + } + }() + return done +} + +func (dr *DashboardServiceImpl) executeCleanupWithLock(ctx context.Context) error { + // We're taking a leader-like locking approach here. By locking and executing, but never releasing the lock, + // we ensure that other instances of this service can't run in parallel and hence the cleanup will only happen once + // per cleanup interval by setting the maxInterval and having the time between executions be the cleanup interval as well. + return dr.serverLockService.LockAndExecute( + ctx, + k8sDashboardKvNamespace, + dr.cfg.K8sDashboardCleanup.Interval, + func(ctx context.Context) { + if err := dr.cleanupK8sDashboardResources(ctx, dr.cfg.K8sDashboardCleanup.BatchSize, dr.cfg.K8sDashboardCleanup.Timeout); err != nil { + dr.log.Error("Failed to cleanup k8s dashboard resources", "error", err) + } + }, + ) +} + +// cleanupK8sDashboardResources cleans up resources marked for deletion in the k8s API. +// It processes all organizations, finds dashboards with the trash label, and cleans them up. +// batchSize specifies how many dashboards to process in a single batch. +// timeout specifies the timeout duration for the cleanup operation. +func (dr *DashboardServiceImpl) cleanupK8sDashboardResources(ctx context.Context, batchSize int64, timeout time.Duration) error { + ctx, span := tracer.Start(ctx, "dashboards.service.cleanupK8sDashboardResources") + defer span.End() + + if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { + return nil + } + + // Create a timeout context to ensure we complete before the lock expires + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{}) + if err != nil { + return err + } + dr.log.Debug("Running k8s dashboard resource cleanup for all orgs", "numOrgs", len(orgs)) + + var errs []error + for _, org := range orgs { + // Check if we're approaching the timeout + if ctx.Err() != nil { + dr.log.Info("Timeout reached during cleanup, stopping processing", "timeout", timeout) + break + } + + orgErr := dr.cleanupOrganizationK8sDashboards(ctx, org.ID, batchSize) + if orgErr != nil { + errs = append(errs, fmt.Errorf("org %d: %w", org.ID, orgErr)) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + return nil +} + +// cleanupOrganizationK8sDashboards handles cleanup for a single organization's Kubernetes dashboards +func (dr *DashboardServiceImpl) cleanupOrganizationK8sDashboards(ctx context.Context, orgID int64, batchSize int64) error { + dr.log.Debug("Running k8s dashboard resource cleanup for org", "orgID", orgID) + + ctx, span := tracer.Start(ctx, "dashboards.service.cleanupK8sDashboardResources.org") + defer span.End() + span.SetAttributes(attribute.Int64("org_id", orgID)) + + ctx, _ = identity.WithServiceIdentity(ctx, orgID) + + // Get the last processed resource version + lastResourceVersion, err := dr.getLastResourceVersion(ctx, orgID) + if err != nil { + return err + } + + var errs []error + continueToken := "" + itemsProcessed := 0 + + for { + // Check if we're approaching the timeout + if ctx.Err() != nil { + dr.log.Info("Timeout reached during org cleanup, stopping processing", "orgID", orgID) + break + } + + // List resources to be cleaned up + data, listErr, shouldContinue := dr.listResourcesToCleanup(ctx, orgID, lastResourceVersion, continueToken, batchSize) + if listErr != nil { + errs = append(errs, fmt.Errorf("failed to list resources: %w", listErr)) + break + } + if shouldContinue { + // Reset and try again with updated resource version + lastResourceVersion = "0" + continueToken = "" + continue + } + + // Skip the first item if it matches our last resource version (due to NotOlderThan behavior) + if len(data.Items) > 0 && data.Items[0].GetResourceVersion() == lastResourceVersion { + data.Items = data.Items[1:] + } + + if len(data.Items) == 0 { + dr.log.Debug("No items to clean up in this batch", "orgID", orgID) + break + } + + dr.log.Info("Processing dashboard cleanup batch", "orgID", orgID, "count", len(data.Items)) + + // Process the batch + processedItems, processingErrs := dr.processDashboardBatch(ctx, orgID, data.Items) + if len(processingErrs) > 0 { + errs = append(errs, processingErrs...) + } + itemsProcessed += processedItems + + // Update resource version after the batch + if len(data.Items) > 0 { + maxBatchResourceVersion := data.Items[len(data.Items)-1].GetResourceVersion() + if lastResourceVersion != maxBatchResourceVersion { + dr.log.Info("Updating resource version after batch", "orgID", orgID, + "newResourceVersion", maxBatchResourceVersion, "oldResourceVersion", lastResourceVersion) + + if updateErr := dr.kvstore.Set(ctx, orgID, k8sDashboardKvNamespace, + k8sDashboardKvLastResourceVersionKey, maxBatchResourceVersion); updateErr != nil { + errs = append(errs, fmt.Errorf("failed to update resource version: %w", updateErr)) + } + } + } + + meta, _ := data.Object["metadata"].(map[string]interface{}) + continueToken, _ = meta["continue"].(string) + if continueToken == "" { + break + } + } + + if itemsProcessed > 0 { + dr.log.Info("Finished k8s dashboard resources cleanup", "orgID", orgID, "itemsProcessed", itemsProcessed) + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} + +// getLastResourceVersion retrieves the last processed resource version from kvstore +func (dr *DashboardServiceImpl) getLastResourceVersion(ctx context.Context, orgID int64) (string, error) { + lastResourceVersion, ok, err := dr.kvstore.Get(ctx, orgID, k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey) + if err != nil { + return "", fmt.Errorf("failed to get last resource version: %w", err) + } + + if !ok { + dr.log.Info("No last resource version found, starting from scratch", "orgID", orgID) + return "0", nil + } + + return lastResourceVersion, nil +} + +// listResourcesToCleanup lists resources that need to be cleaned up +func (dr *DashboardServiceImpl) listResourcesToCleanup(ctx context.Context, orgID int64, resourceVersion, continueToken string, batchSize int64) (*unstructured.UnstructuredList, error, bool) { + var listOptions v1.ListOptions + if continueToken != "" { + listOptions = v1.ListOptions{ + LabelSelector: utils.LabelKeyGetTrash + "=true", + Continue: continueToken, + Limit: batchSize, + } + } else { + listOptions = v1.ListOptions{ + LabelSelector: utils.LabelKeyGetTrash + "=true", + ResourceVersionMatch: v1.ResourceVersionMatchNotOlderThan, + ResourceVersion: resourceVersion, + Limit: batchSize, + } + } + + data, err := dr.k8sclient.List(ctx, orgID, listOptions) + if err != nil { + if strings.Contains(err.Error(), "too old resource version") { + // If the resource version is too old, start from the current version + dr.log.Info("Resource version too old, starting from current version", "orgID", orgID) + return nil, nil, true // Signal to continue with reset version + } + return nil, err, false + } + + return data, nil, false +} + +// processDashboardBatch processes a batch of dashboards for cleanup +func (dr *DashboardServiceImpl) processDashboardBatch(ctx context.Context, orgID int64, items []unstructured.Unstructured) (int, []error) { + var errs []error + itemsProcessed := 0 + + for _, item := range items { + dash, err := dr.UnstructuredToLegacyDashboard(ctx, &item, orgID) + if err != nil { + errs = append(errs, fmt.Errorf("failed to convert dashboard: %w", err)) + continue + } + + meta, _ := item.Object["metadata"].(map[string]interface{}) + deletionTimestamp, _ := meta["deletionTimestamp"].(string) + resourceVersion, _ := meta["resourceVersion"].(string) + + dr.log.Info("K8s dashboard resource previously got deleted, cleaning up", + "UID", dash.UID, + "orgID", orgID, + "deletionTimestamp", deletionTimestamp, + "resourceVersion", resourceVersion) + + if err = dr.CleanUpDashboard(ctx, dash.UID, orgID); err != nil { + errs = append(errs, fmt.Errorf("failed to clean up dashboard %s: %w", dash.UID, err)) + } + itemsProcessed++ + } + + return itemsProcessed, errs +} + +// This gets auto-invoked when grafana starts, part of the BackgroundService interface +func (dr *DashboardServiceImpl) Run(ctx context.Context) error { + cleanupBackgroundJobStopped := dr.startK8sDeletedDashboardsCleanupJob(ctx) + <-ctx.Done() + // Wait for cleanup job to finish + <-cleanupBackgroundJobStopped + return ctx.Err() +} + var _ dashboards.PermissionsRegistrationService = (*DashboardServiceImpl)(nil) +var _ registry.BackgroundService = (*DashboardServiceImpl)(nil) // This is the uber service that implements a three smaller services func ProvideDashboardServiceImpl( @@ -96,6 +365,8 @@ func ProvideDashboardServiceImpl( restConfigProvider apiserver.RestConfigProvider, userService user.Service, quotaService quota.Service, orgService org.Service, publicDashboardService publicdashboards.ServiceWrapper, resourceClient resource.ResourceClient, dual dualwrite.Service, sorter sort.Service, + serverLockService *serverlock.ServerLockService, + kvstore kvstore.KVStore, ) (*DashboardServiceImpl, error) { k8sHandler := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), dashboardv0alpha1.DashboardResourceInfo.GroupVersionResource(), restConfigProvider.GetRestConfig, dashboardStore, userService, resourceClient, sorter) @@ -113,6 +384,8 @@ func ProvideDashboardServiceImpl( metrics: newDashboardsMetrics(r), dashboardPermissionsReady: make(chan struct{}), publicDashboardService: publicDashboardService, + serverLockService: serverLockService, + kvstore: kvstore, } defaultLimits, err := readQuotaConfig(cfg) @@ -860,18 +1133,7 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId cmd := &dashboards.DeleteDashboardCommand{OrgID: orgId, ID: dashboardId, UID: dashboardUID} if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { - err := dr.deleteDashboardThroughK8s(ctx, cmd, validateProvisionedDashboard) - if err != nil { - return err - } - - // cleanup things related to dashboards that are not stored in unistore yet - err = dr.publicDashboardService.DeleteByDashboardUIDs(ctx, orgId, []string{dashboardUID}) - if err != nil { - return err - } - - return dr.dashboardStore.CleanupAfterDelete(ctx, cmd) + return dr.deleteDashboardThroughK8s(ctx, cmd, validateProvisionedDashboard) } if validateProvisionedDashboard { @@ -1494,6 +1756,19 @@ func (dr *DashboardServiceImpl) DeleteInFolders(ctx context.Context, orgID int64 func (dr *DashboardServiceImpl) Kind() string { return entity.StandardKindDashboard } +func (dr *DashboardServiceImpl) CleanUpDashboard(ctx context.Context, dashboardUID string, orgId int64) error { + ctx, span := tracer.Start(ctx, "dashboards.service.CleanUpDashboard") + defer span.End() + + // cleanup things related to dashboards that are not stored in unistore yet + var err = dr.publicDashboardService.DeleteByDashboardUIDs(ctx, orgId, []string{dashboardUID}) + if err != nil { + return err + } + + return dr.dashboardStore.CleanupAfterDelete(ctx, &dashboards.DeleteDashboardCommand{OrgID: orgId, UID: dashboardUID}) +} + func (dr *DashboardServiceImpl) CleanUpDeletedDashboards(ctx context.Context) (int64, error) { ctx, span := tracer.Start(ctx, "dashboards.service.CleanUpDeletedDashboards") defer span.End() diff --git a/pkg/services/dashboards/service/dashboard_service_integration_test.go b/pkg/services/dashboards/service/dashboard_service_integration_test.go index e22a5ae0cd4..7887e628110 100644 --- a/pkg/services/dashboards/service/dashboard_service_integration_test.go +++ b/pkg/services/dashboards/service/dashboard_service_integration_test.go @@ -11,6 +11,8 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -813,6 +815,8 @@ func permissionScenario(t *testing.T, desc string, fn permissionScenarioFunc) { nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) dashboardService.RegisterDashboardPermissions(dashboardPermissions) require.NoError(t, err) @@ -906,6 +910,8 @@ func callSaveWithResult(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSt nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) service.RegisterDashboardPermissions(dashboardPermissions) @@ -977,6 +983,8 @@ func saveTestDashboard(t *testing.T, title string, orgID int64, folderUID string nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) service.RegisterDashboardPermissions(dashboardPermissions) @@ -1056,6 +1064,8 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) service.RegisterDashboardPermissions(accesscontrolmock.NewMockedPermissionsService()) diff --git a/pkg/services/dashboards/service/dashboard_service_test.go b/pkg/services/dashboards/service/dashboard_service_test.go index 78c38656329..82600d9f2cd 100644 --- a/pkg/services/dashboards/service/dashboard_service_test.go +++ b/pkg/services/dashboards/service/dashboard_service_test.go @@ -18,7 +18,10 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" + "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" @@ -33,6 +36,7 @@ import ( "github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/search/model" "github.com/grafana/grafana/pkg/services/search/sort" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/storage/unified/resource" @@ -833,9 +837,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { _, k8sCliMock := setupK8sDashboardTests(service) k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default") k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - fakeStore.On("CleanupAfterDelete", mock.Anything, &dashboards.DeleteDashboardCommand{UID: "uid", OrgID: 1}).Return(nil).Once() - fakeStore.On("CleanupAfterDelete", mock.Anything, &dashboards.DeleteDashboardCommand{UID: "uid3", OrgID: 2}).Return(nil).Once() - fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil) k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{ "metadata": map[string]any{ "name": "uid", @@ -1033,8 +1034,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { // Mock deleteDashboard() k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil) - fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Once() // Mock WaitForSearchQuery() // First call returns 1 hit @@ -1476,8 +1475,6 @@ func TestDeleteDashboard(t *testing.T) { t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) { ctx, k8sCliMock := setupK8sDashboardTests(service) k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Once() - fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() err := service.DeleteDashboard(ctx, 1, "uid", 1) require.NoError(t, err) @@ -1488,8 +1485,6 @@ func TestDeleteDashboard(t *testing.T) { ctx, k8sCliMock := setupK8sDashboardTests(service) k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default") k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Once() - fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{ @@ -2314,3 +2309,335 @@ func TestLegacySaveCommandToUnstructured(t *testing.T) { assert.Equal(t, result.GetAnnotations(), map[string]string(nil)) }) } + +func TestCleanUpDashboard(t *testing.T) { + tests := []struct { + name string + deleteError error + cleanupError error + expectCleanup bool + expectedError error + }{ + { + name: "Should delete public dashboards and clean up after delete", + expectCleanup: true, + }, + { + name: "Should return error if DeleteByDashboardUIDs fails", + deleteError: fmt.Errorf("deletion error"), + expectCleanup: false, + expectedError: fmt.Errorf("deletion error"), + }, + { + name: "Should return error if CleanupAfterDelete fails", + cleanupError: fmt.Errorf("cleanup error"), + expectCleanup: true, + expectedError: fmt.Errorf("cleanup error"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + fakeStore := dashboards.FakeDashboardStore{} + fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t) + service := &DashboardServiceImpl{ + cfg: setting.NewCfg(), + dashboardStore: &fakeStore, + publicDashboardService: fakePublicDashboardService, + } + + ctx := context.Background() + dashboardUID := "dash-uid" + orgID := int64(1) + + // Setup mocks + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, orgID, []string{dashboardUID}).Return(tc.deleteError).Maybe() + + if tc.expectCleanup { + fakeStore.On("CleanupAfterDelete", mock.Anything, &dashboards.DeleteDashboardCommand{ + OrgID: orgID, + UID: dashboardUID, + }).Return(tc.cleanupError).Maybe() + } + + // Execute + err := service.CleanUpDashboard(ctx, dashboardUID, orgID) + + // Assert + if tc.expectedError != nil { + require.Error(t, err) + require.Equal(t, tc.expectedError.Error(), err.Error()) + } else { + require.NoError(t, err) + } + + fakePublicDashboardService.AssertExpectations(t) + fakeStore.AssertExpectations(t) + }) + } +} + +func TestK8sDashboardCleanupJob(t *testing.T) { + tests := []struct { + name string + featureEnabled bool + batchSize int + setupFunc func(*DashboardServiceImpl, context.Context, *client.MockK8sHandler) + verifyFunc func(*testing.T, *DashboardServiceImpl, context.Context, *client.MockK8sHandler, *kvstore.FakeKVStore) + }{ + { + name: "Should not run cleanup when feature flag is disabled", + featureEnabled: false, + batchSize: 10, + }, + { + name: "Should process dashboard cleanup for all orgs", + featureEnabled: true, + batchSize: 10, + setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) { + // Test organizations + fakeOrgService := service.orgService.(*orgtest.FakeOrgService) + fakeOrgService.ExpectedOrgs = []*org.OrgDTO{ + {ID: 1, Name: "org1"}, + {ID: 2, Name: "org2"}, + } + + kv := service.kvstore.(*kvstore.FakeKVStore) + fakeStore := service.dashboardStore.(*dashboards.FakeDashboardStore) + fakePublicDashboardService := service.publicDashboardService.(*publicdashboards.FakePublicDashboardServiceWrapper) + + // Create dashboard unstructured items for response + dashboard1 := createTestUnstructuredDashboard("dash1", "org1-dashboard", "101") + dashboard2 := createTestUnstructuredDashboard("dash2", "org2-dashboard", "201") + + // Setup test data in KV store. Only populate org 1. + _ = kv.Set(ctx, int64(1), k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey, "100") + + // Mock K8s responses for org 1 + k8sCliMock.On("List", mock.AnythingOfType("*context.valueCtx"), int64(1), mock.MatchedBy(func(opts metav1.ListOptions) bool { + return opts.LabelSelector == utils.LabelKeyGetTrash+"=true" && + opts.Continue == "" + })).Return(&unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "resourceVersion": "101", + }, + }, + Items: []unstructured.Unstructured{dashboard1}, + }, nil).Once() + + // Mock K8s responses for org 2 + k8sCliMock.On("List", mock.AnythingOfType("*context.valueCtx"), int64(2), mock.MatchedBy(func(opts metav1.ListOptions) bool { + return opts.LabelSelector == utils.LabelKeyGetTrash+"=true" && + opts.Continue == "" + })).Return(&unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "resourceVersion": "201", + }, + }, + Items: []unstructured.Unstructured{dashboard2}, + }, nil).Once() + + // Mock GetUserFromMeta calls + k8sCliMock.On("GetUserFromMeta", mock.AnythingOfType("*context.valueCtx"), mock.Anything).Return(&user.User{}, nil).Times(4) + + // Mock cleanup + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash1"}).Return(nil).Once() + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(2), []string{"dash2"}).Return(nil).Once() + fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Times(2) + }, + verifyFunc: func(t *testing.T, service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler, kv *kvstore.FakeKVStore) { + k8sCliMock.AssertExpectations(t) + + // Verify KV store was updated with new resource versions + val1, found1, _ := kv.Get(ctx, int64(1), k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey) + require.True(t, found1) + require.Equal(t, "101", val1) + + val2, found2, _ := kv.Get(ctx, int64(2), k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey) + require.True(t, found2) + require.Equal(t, "201", val2) + }, + }, + { + name: "Should handle pagination and batching when processing large sets of dashboards", + featureEnabled: true, + batchSize: 3, + setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) { + // Test organization + fakeOrgService := service.orgService.(*orgtest.FakeOrgService) + fakeOrgService.ExpectedOrgs = []*org.OrgDTO{ + {ID: 1, Name: "org1"}, + } + + kv := service.kvstore.(*kvstore.FakeKVStore) + fakeStore := service.dashboardStore.(*dashboards.FakeDashboardStore) + fakePublicDashboardService := service.publicDashboardService.(*publicdashboards.FakePublicDashboardServiceWrapper) + + // Setup initial resource version + initialVersion := "100" + _ = kv.Set(ctx, int64(1), k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey, initialVersion) + + // Create dashboard batches (5 dashboards total, to be processed in 2 batches) + firstBatch := []unstructured.Unstructured{ + createTestUnstructuredDashboard("dash1", "dashboard1", "101"), + createTestUnstructuredDashboard("dash2", "dashboard2", "102"), + createTestUnstructuredDashboard("dash3", "dashboard3", "150"), + } + secondBatch := []unstructured.Unstructured{ + createTestUnstructuredDashboard("dash4", "dashboard4", "180"), + createTestUnstructuredDashboard("dash5", "dashboard5", "200"), + } + + // First batch response with continue token + k8sCliMock.On("List", mock.AnythingOfType("*context.valueCtx"), int64(1), mock.MatchedBy(func(opts metav1.ListOptions) bool { + return opts.LabelSelector == utils.LabelKeyGetTrash+"=true" && + opts.Continue == "" + })).Return(&unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "resourceVersion": "200", + "continue": "next-token", + }, + }, + Items: firstBatch, + }, nil).Once() + + // Second batch response with updated resource version + k8sCliMock.On("List", mock.AnythingOfType("*context.valueCtx"), int64(1), mock.MatchedBy(func(opts metav1.ListOptions) bool { + return opts.LabelSelector == utils.LabelKeyGetTrash+"=true" && + opts.Continue == "next-token" + })).Return(&unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "resourceVersion": "200", + }, + }, + Items: secondBatch, + }, nil).Once() + + // Mock GetUserFromMeta calls for each dashboard + k8sCliMock.On("GetUserFromMeta", mock.AnythingOfType("*context.valueCtx"), mock.Anything).Return(&user.User{}, nil).Times(10) + + // Mock public dashboard deletion for each dashboard + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash1"}).Return(nil).Once() + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash2"}).Return(nil).Once() + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash3"}).Return(nil).Once() + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash4"}).Return(nil).Once() + fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"dash5"}).Return(nil).Once() + + // Mock cleanup after delete for each dashboard + fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Times(5) + }, + verifyFunc: func(t *testing.T, service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler, kv *kvstore.FakeKVStore) { + k8sCliMock.AssertExpectations(t) + + // Verify KV store was updated with latest resource version + val, found, _ := kv.Get(ctx, int64(1), k8sDashboardKvNamespace, k8sDashboardKvLastResourceVersionKey) + require.True(t, found) + require.Equal(t, "200", val) + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test database and utilities + sqlStore, _ := sqlstore.InitTestDB(t) + lockService := serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()) + kv := kvstore.NewFakeKVStore() + + fakeStore := dashboards.FakeDashboardStore{} + fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t) + fakeOrgService := orgtest.NewOrgServiceFake() + + features := featuremgmt.WithFeatures() + if tc.featureEnabled { + features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders) + } + + service := &DashboardServiceImpl{ + cfg: setting.NewCfg(), + log: log.New("test.logger"), + dashboardStore: &fakeStore, + publicDashboardService: fakePublicDashboardService, + orgService: fakeOrgService, + serverLockService: lockService, + kvstore: kv, + features: features, + } + + ctx, k8sCliMock := setupK8sDashboardTests(service) + + if tc.setupFunc != nil { + tc.setupFunc(service, ctx, k8sCliMock) + } + + // Execute + err := service.cleanupK8sDashboardResources(ctx, int64(tc.batchSize), 20*time.Second) + require.NoError(t, err) + + if tc.verifyFunc != nil { + tc.verifyFunc(t, service, ctx, k8sCliMock, kv) + } + }) + } + + t.Run("Should start and stop background job correctly", func(t *testing.T) { + // Setup test database and utilities + sqlStore, _ := sqlstore.InitTestDB(t) + lockService := serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()) + + cfg := setting.NewCfg() + cfg.K8sDashboardCleanup = setting.K8sDashboardCleanupSettings{ + Interval: 30 * time.Second, + Timeout: 25 * time.Second, + BatchSize: 10, + } + + service := &DashboardServiceImpl{ + cfg: cfg, + log: log.New("test.logger"), + features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders), + serverLockService: lockService, + } + + // Create a test context that can be canceled + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Start job with the context + done := service.startK8sDeletedDashboardsCleanupJob(ctx) + + // Cancel context to verify graceful shutdown + cancel() + + // Wait for goroutine to exit instead of using sleep + select { + case <-done: + // Job exited successfully + case <-time.After(time.Second): + t.Fatal("Cleanup job didn't exit within timeout") + } + }) +} + +// Helper functions for testing + +func createTestUnstructuredDashboard(uid, title string, resourceVersion string) unstructured.Unstructured { + return unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(), + "kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind, + "metadata": map[string]interface{}{ + "name": uid, + "deletionTimestamp": "2023-01-01T00:00:00Z", + "resourceVersion": resourceVersion, + }, + "spec": map[string]interface{}{ + "title": title, + }, + }, + } +} diff --git a/pkg/services/dashboardsnapshots/service/service_test.go b/pkg/services/dashboardsnapshots/service/service_test.go index 9c4fedb47e4..c647528785b 100644 --- a/pkg/services/dashboardsnapshots/service/service_test.go +++ b/pkg/services/dashboardsnapshots/service/service_test.go @@ -11,6 +11,9 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/serverlock" + "github.com/grafana/grafana/pkg/infra/tracing" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/apiserver/client" "github.com/grafana/grafana/pkg/services/dashboards" @@ -121,6 +124,8 @@ func TestValidateDashboardExists(t *testing.T) { nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) s := ProvideService(dsStore, secretsService, dashSvc) diff --git a/pkg/services/folder/folderimpl/folder_test.go b/pkg/services/folder/folderimpl/folder_test.go index b6361936a5d..fcb88a922cf 100644 --- a/pkg/services/folder/folderimpl/folder_test.go +++ b/pkg/services/folder/folderimpl/folder_test.go @@ -20,8 +20,10 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db/dbtest" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log/logtest" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -498,7 +500,10 @@ func TestIntegrationNestedFolderService(t *testing.T) { publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil) dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuresFlagOn, folderPermissions, ac, serviceWithFlagOn, nestedFolderStore, nil, - client.MockTestRestConfig{}, nil, quotaService, nil, publicDashboardFakeService, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + client.MockTestRestConfig{}, nil, quotaService, nil, publicDashboardFakeService, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), + ) require.NoError(t, err) dashSrv.RegisterDashboardPermissions(dashboardPermissions) @@ -584,7 +589,10 @@ func TestIntegrationNestedFolderService(t *testing.T) { publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil) dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuresFlagOff, - folderPermissions, ac, serviceWithFlagOff, nestedFolderStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, publicDashboardFakeService, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + folderPermissions, ac, serviceWithFlagOff, nestedFolderStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, publicDashboardFakeService, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), + ) require.NoError(t, err) dashSrv.RegisterDashboardPermissions(dashboardPermissions) alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOff, dashSrv, ac, b) @@ -729,7 +737,10 @@ func TestIntegrationNestedFolderService(t *testing.T) { dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, tc.featuresFlag, folderPermissions, ac, tc.service, tc.service.store, nil, client.MockTestRestConfig{}, nil, quotaService, nil, publicDashboardFakeService, nil, - dualwrite.ProvideTestService(), sort.ProvideService()) + dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), + ) require.NoError(t, err) dashSrv.RegisterDashboardPermissions(dashboardPermissions) @@ -1524,6 +1535,8 @@ func TestIntegrationNestedFolderSharedWithMe(t *testing.T) { nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) dashboardService.RegisterDashboardPermissions(dashboardPermissions) diff --git a/pkg/services/libraryelements/libraryelements_test.go b/pkg/services/libraryelements/libraryelements_test.go index ee1d1fe7ec6..8eec5685b14 100644 --- a/pkg/services/libraryelements/libraryelements_test.go +++ b/pkg/services/libraryelements/libraryelements_test.go @@ -18,7 +18,9 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/kinds/librarypanel" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -366,6 +368,8 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) service.RegisterDashboardPermissions(dashboardPermissions) @@ -459,6 +463,8 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena features, folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, svcErr) dashboardService.RegisterDashboardPermissions(dashboardPermissions) @@ -532,6 +538,8 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo features, folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, dashSvcErr) dashService.RegisterDashboardPermissions(dashboardPermissions) diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index 531f3714eca..930ec705e64 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -15,7 +15,9 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/kinds/librarypanel" @@ -739,7 +741,8 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash features, acmock.NewMockedPermissionsService(), ac, foldertest.NewFakeService(), folder.NewFakeStore(), nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), - ) + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) service.RegisterDashboardPermissions(dashPermissionService) dashboard, err := service.SaveDashboard(context.Background(), dashItem, true) @@ -837,7 +840,8 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo features, acmock.NewMockedPermissionsService(), ac, folderSvc, folder.NewFakeStore(), nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), - ) + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) dashService.RegisterDashboardPermissions(dashPermissionService) guardian.InitAccessControlGuardian(cfg, ac, dashService, folderSvc, log.NewNopLogger()) diff --git a/pkg/services/ngalert/testutil/testutil.go b/pkg/services/ngalert/testutil/testutil.go index 0e13a46834d..daa61da93d3 100644 --- a/pkg/services/ngalert/testutil/testutil.go +++ b/pkg/services/ngalert/testutil/testutil.go @@ -8,6 +8,8 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" @@ -67,6 +69,8 @@ func SetupDashboardService(tb testing.TB, sqlStore db.DB, fs *folderimpl.Dashboa foldertest.NewFakeService(), folder.NewFakeStore(), nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(tb, err) dashboardService.RegisterDashboardPermissions(dashboardPermissions) diff --git a/pkg/services/publicdashboards/api/query_test.go b/pkg/services/publicdashboards/api/query_test.go index 8c27d18b0ea..ddbf2925d4b 100644 --- a/pkg/services/publicdashboards/api/query_test.go +++ b/pkg/services/publicdashboards/api/query_test.go @@ -21,8 +21,11 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" + "github.com/grafana/grafana/pkg/infra/tracing" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/annotations/annotationstest" "github.com/grafana/grafana/pkg/services/apiserver/client" @@ -330,6 +333,8 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T) featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), ac, foldertest.NewFakeService(), folder.NewFakeStore(), nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(db, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore(), ) require.NoError(t, err) dashService.RegisterDashboardPermissions(dashPermissionService) diff --git a/pkg/services/publicdashboards/service/service_test.go b/pkg/services/publicdashboards/service/service_test.go index 67feb554cf2..e8ea78749a6 100644 --- a/pkg/services/publicdashboards/service/service_test.go +++ b/pkg/services/publicdashboards/service/service_test.go @@ -19,6 +19,8 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" @@ -1400,7 +1402,9 @@ func TestPublicDashboardServiceImpl_ListPublicDashboards(t *testing.T) { fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, nil, testDB, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService()) - dashboardService, err := dashsvc.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + dashboardService, err := dashsvc.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), folderPermissions, ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotatest.New(false, nil), nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(testDB, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) dashboardService.RegisterDashboardPermissions(&actest.FakePermissionsService{}) diff --git a/pkg/services/quota/quotaimpl/quota_test.go b/pkg/services/quota/quotaimpl/quota_test.go index 6100f1f0e6a..c8cc6d5dfde 100644 --- a/pkg/services/quota/quotaimpl/quota_test.go +++ b/pkg/services/quota/quotaimpl/quota_test.go @@ -12,7 +12,9 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/httpclient" + "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" pluginfakes "github.com/grafana/grafana/pkg/plugins/manager/fakes" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -499,7 +501,9 @@ func setupEnv(t *testing.T, sqlStore db.DB, cfg *setting.Cfg, b bus.Bus, quotaSe fStore, acmock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, nil, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService()) dashService, err := dashService.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), - ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService()) + ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(), + serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()), + kvstore.NewFakeKVStore()) require.NoError(t, err) dashService.RegisterDashboardPermissions(acmock.NewMockedPermissionsService()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 815553d3813..59fe66d9175 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -181,6 +181,9 @@ type Cfg struct { DataProxyWhiteList map[string]bool ActionsAllowPostURL string + // K8s Dashboard Cleanup + K8sDashboardCleanup K8sDashboardCleanupSettings + TempDataLifetime time.Duration // Plugins @@ -1291,6 +1294,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error { cfg.readDataSourcesSettings() cfg.readDataSourceSecuritySettings() + cfg.readK8sDashboardCleanupSettings() cfg.readSqlDataSourceSettings() cfg.Storage = readStorageSettings(iniFile) diff --git a/pkg/setting/setting_k8s_dashboard_cleanup.go b/pkg/setting/setting_k8s_dashboard_cleanup.go new file mode 100644 index 00000000000..660de43c388 --- /dev/null +++ b/pkg/setting/setting_k8s_dashboard_cleanup.go @@ -0,0 +1,53 @@ +package setting + +import ( + "time" +) + +type K8sDashboardCleanupSettings struct { + Interval time.Duration + Timeout time.Duration + BatchSize int64 +} + +const ( + defaultK8sDashboardCleanupInterval = 30 * time.Second + defaultK8sDashboardCleanupBatchSize = int64(10) + minK8sDashboardCleanupInterval = 10 * time.Second + minK8sDashboardCleanupTimeout = 5 * time.Second + minK8sDashboardCleanupBatchSize = int64(5) + maxK8sDashboardCleanupBatchSize = int64(200) +) + +func (cfg *Cfg) readK8sDashboardCleanupSettings() { + section := cfg.Raw.Section("dashboard_cleanup") + + // Read interval setting with validation + cleanupInterval := section.Key("interval").MustDuration(defaultK8sDashboardCleanupInterval) + if cleanupInterval < minK8sDashboardCleanupInterval { + cfg.Logger.Warn("[dashboard_cleanup.interval] is too low; the minimum allowed (10s) is enforced") + cleanupInterval = minK8sDashboardCleanupInterval + } + + // Calculate timeout as 5 seconds less than interval, with minimum validation + cleanupTimeout := cleanupInterval - (5 * time.Second) + if cleanupTimeout < minK8sDashboardCleanupTimeout { + cleanupTimeout = minK8sDashboardCleanupTimeout + } + + // Read batch size with validation + batchSize := section.Key("batch_size").MustInt64(defaultK8sDashboardCleanupBatchSize) + if batchSize < minK8sDashboardCleanupBatchSize { + cfg.Logger.Warn("[dashboard_cleanup.batch_size] is too low; the minimum allowed (5) is enforced") + batchSize = minK8sDashboardCleanupBatchSize + } else if batchSize > maxK8sDashboardCleanupBatchSize { + cfg.Logger.Warn("[dashboard_cleanup.batch_size] is too high; the maximum allowed (1000) is enforced") + batchSize = maxK8sDashboardCleanupBatchSize + } + + cfg.K8sDashboardCleanup = K8sDashboardCleanupSettings{ + Interval: cleanupInterval, + Timeout: cleanupTimeout, + BatchSize: batchSize, + } +} From 9c0c9359a0b130403d8d38ab482b1ac2d502cae6 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Mon, 24 Mar 2025 08:17:11 +0100 Subject: [PATCH 12/86] CloudMigrations: Refactor folder name resolution when fetching migration json data (#102604) * CloudMigrations: Refactor folder name resolution when fetching migration json data * Update pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt.go --------- Co-authored-by: Michael Mandrus <41969079+mmandrus@users.noreply.github.com> --- .../cloudmigrationimpl/cloudmigration_test.go | 100 ++++++--- .../cloudmigrationimpl/snapshot_mgmt.go | 212 +++++++++--------- 2 files changed, 169 insertions(+), 143 deletions(-) diff --git a/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go b/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go index 246e74c6da7..b8f30c1cab4 100644 --- a/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go +++ b/pkg/services/cloudmigration/cloudmigrationimpl/cloudmigration_test.go @@ -628,62 +628,92 @@ func TestGetFolderNamesForFolderUIDs(t *testing.T) { func TestGetParentNames(t *testing.T) { t.Parallel() - s := setUpServiceTest(t, false).(*Service) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) + s := setUpServiceTest(t, false).(*Service) + user := &user.SignedInUser{OrgID: 1} - libraryElementFolderUID := "folderUID-A" + testcases := []struct { + name string fakeFolders []*folder.Folder - folders []folder.CreateFolderCommand - dashboards []dashboards.Dashboard - libraryElements []libraryElement - alertRules []alertRule - expectedParentNames map[cloudmigration.MigrateDataType][]string + folderHierarchy map[cloudmigration.MigrateDataType]map[string]string + expectedParentNames map[cloudmigration.MigrateDataType]map[string]string }{ { + name: "multiple data types", + fakeFolders: []*folder.Folder{ + {UID: "folderUID-A", Title: "Folder A", OrgID: 1}, + {UID: "folderUID-B", Title: "Folder B", OrgID: 1}, + {UID: "folderUID-C", Title: "Folder C", OrgID: 1}, + }, + folderHierarchy: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "folderUID-A", "dashboard-2": "folderUID-B", "dashboard-3": ""}, + cloudmigration.LibraryElementDataType: {"libElement-1": "folderUID-A", "libElement-2": "folderUID-C"}, + cloudmigration.AlertRuleType: {"alertRule-1": "folderUID-B"}, + }, + expectedParentNames: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "Folder A", "dashboard-2": "Folder B", "dashboard-3": ""}, + cloudmigration.LibraryElementDataType: {"libElement-1": "Folder A", "libElement-2": "Folder C"}, + cloudmigration.AlertRuleType: {"alertRule-1": "Folder B"}, + }, + }, + { + name: "empty folder hierarchy", + fakeFolders: []*folder.Folder{ + {UID: "folderUID-A", Title: "Folder A", OrgID: 1}, + }, + folderHierarchy: map[cloudmigration.MigrateDataType]map[string]string{}, + expectedParentNames: map[cloudmigration.MigrateDataType]map[string]string{}, + }, + { + name: "all root folders (no parents)", fakeFolders: []*folder.Folder{ - {UID: "folderUID-A", Title: "Folder A", OrgID: 1, ParentUID: ""}, - {UID: "folderUID-B", Title: "Folder B", OrgID: 1, ParentUID: "folderUID-A"}, - {UID: "folderUID-X", Title: "Folder X", OrgID: 1, ParentUID: ""}, + {UID: "folderUID-A", Title: "Folder A", OrgID: 1}, }, - folders: []folder.CreateFolderCommand{ - {UID: "folderUID-C", Title: "Folder A", OrgID: 1, ParentUID: "folderUID-A"}, + folderHierarchy: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "", "dashboard-2": ""}, + cloudmigration.LibraryElementDataType: {"libElement-1": ""}, }, - dashboards: []dashboards.Dashboard{ - {UID: "dashboardUID-0", OrgID: 1, FolderUID: ""}, - {UID: "dashboardUID-1", OrgID: 1, FolderUID: "folderUID-A"}, - {UID: "dashboardUID-2", OrgID: 1, FolderUID: "folderUID-B"}, + expectedParentNames: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "", "dashboard-2": ""}, + cloudmigration.LibraryElementDataType: {"libElement-1": ""}, }, - libraryElements: []libraryElement{ - {UID: "libraryElementUID-0", FolderUID: &libraryElementFolderUID}, - {UID: "libraryElementUID-1"}, + }, + { + name: "non-existent folder UIDs", + fakeFolders: []*folder.Folder{ + {UID: "folderUID-A", Title: "Folder A", OrgID: 1}, }, - alertRules: []alertRule{ - {UID: "alertRuleUID-0", FolderUID: ""}, - {UID: "alertRuleUID-1", FolderUID: "folderUID-B"}, + folderHierarchy: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "folderUID-A", "dashboard-2": "non-existent-uid"}, }, - expectedParentNames: map[cloudmigration.MigrateDataType][]string{ - cloudmigration.DashboardDataType: {"", "Folder A", "Folder B"}, - cloudmigration.FolderDataType: {"Folder A"}, - cloudmigration.LibraryElementDataType: {"Folder A"}, - cloudmigration.AlertRuleType: {"Folder B"}, + expectedParentNames: map[cloudmigration.MigrateDataType]map[string]string{ + cloudmigration.DashboardDataType: {"dashboard-1": "Folder A", "dashboard-2": ""}, }, }, } for _, tc := range testcases { - s.folderService = &foldertest.FakeService{ExpectedFolders: tc.fakeFolders} + t.Run(tc.name, func(t *testing.T) { + s.folderService = &foldertest.FakeService{ExpectedFolders: tc.fakeFolders} - dataUIDsToParentNamesByType, err := s.getParentNames(ctx, user, tc.dashboards, tc.folders, tc.libraryElements, tc.alertRules) - require.NoError(t, err) + dataUIDsToParentNamesByType, err := s.getParentNames(ctx, user, tc.folderHierarchy) + require.NoError(t, err) - for dataType, expectedParentNames := range tc.expectedParentNames { - actualParentNames := slices.Collect(maps.Values(dataUIDsToParentNamesByType[dataType])) - require.Len(t, actualParentNames, len(expectedParentNames)) - require.ElementsMatch(t, expectedParentNames, actualParentNames) - } + for dataType, expectedParentNames := range tc.expectedParentNames { + actualParentNames := dataUIDsToParentNamesByType[dataType] + + require.Equal(t, len(expectedParentNames), len(actualParentNames)) + + for uid, expectedName := range expectedParentNames { + actualName, exists := actualParentNames[uid] + require.True(t, exists) + require.Equal(t, expectedName, actualName) + } + } + }) } } diff --git a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt.go b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt.go index 034c601f510..e74c9217fbc 100644 --- a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt.go +++ b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt.go @@ -12,6 +12,9 @@ import ( "sort" "time" + "go.opentelemetry.io/otel/codes" + "golang.org/x/crypto/nacl/box" + snapshot "github.com/grafana/grafana-cloud-migration-snapshot/src" "github.com/grafana/grafana-cloud-migration-snapshot/src/contracts" "github.com/grafana/grafana-cloud-migration-snapshot/src/infra/crypto" @@ -29,9 +32,6 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util/retryer" - "golang.org/x/crypto/nacl/box" - - "go.opentelemetry.io/otel/codes" ) var currentMigrationTypes = []cloudmigration.MigrateDataType{ @@ -52,6 +52,10 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S ctx, span := s.tracer.Start(ctx, "CloudMigrationService.getMigrationDataJSON") defer span.End() + migrationDataSlice := make([]cloudmigration.MigrateDataRequestItem, 0) + + folderHierarchy := make(map[cloudmigration.MigrateDataType]map[string]string, 0) + // Plugins plugins, err := s.getPlugins(ctx, signedInUser) if err != nil { @@ -59,74 +63,6 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S return nil, err } - // Data sources - dataSources, err := s.getDataSourceCommands(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get datasources", "err", err) - return nil, err - } - - // Dashboards and folders are linked via the schema, so we need to get both - dashs, folders, err := s.getDashboardAndFolderCommands(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get dashboards and folders", "err", err) - return nil, err - } - - libraryElements, err := s.getLibraryElementsCommands(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get library elements", "err", err) - return nil, err - } - - // Alerts: Mute Timings - muteTimings, err := s.getAlertMuteTimings(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert mute timings", "err", err) - return nil, err - } - - // Alerts: Notification Templates - notificationTemplates, err := s.getNotificationTemplates(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert notification templates", "err", err) - return nil, err - } - - // Alerts: Contact Points - contactPoints, err := s.getContactPoints(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert contact points", "err", err) - return nil, err - } - - // Alerts: Notification Policies - notificationPolicies, err := s.getNotificationPolicies(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert notification policies", "err", err) - return nil, err - } - - // Alerts: Alert Rule Groups - alertRuleGroups, err := s.getAlertRuleGroups(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert rule groups", "err", err) - return nil, err - } - - // Alerts: Alert Rules - alertRules, err := s.getAlertRules(ctx, signedInUser) - if err != nil { - s.log.Error("Failed to get alert rules", "err", err) - return nil, err - } - - migrationDataSlice := make( - []cloudmigration.MigrateDataRequestItem, 0, - len(plugins)+len(dataSources)+len(dashs)+len(folders)+len(libraryElements)+ - len(muteTimings)+len(notificationTemplates)+len(contactPoints)+len(alertRules), - ) - for _, plugin := range plugins { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.PluginDataType, @@ -136,6 +72,13 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Data sources + dataSources, err := s.getDataSourceCommands(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get datasources", "err", err) + return nil, err + } + for _, ds := range dataSources { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.DatasourceDataType, @@ -145,6 +88,15 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Dashboards & Folders: linked via the schema, so we need to get both + dashs, folders, err := s.getDashboardAndFolderCommands(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get dashboards and folders", "err", err) + return nil, err + } + + folderHierarchy[cloudmigration.DashboardDataType] = make(map[string]string, 0) + for _, dashboard := range dashs { dashboard.Data.Del("id") migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ @@ -159,8 +111,12 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S FolderUID: dashboard.FolderUID, }, }) + + folderHierarchy[cloudmigration.DashboardDataType][dashboard.UID] = dashboard.FolderUID } + folderHierarchy[cloudmigration.FolderDataType] = make(map[string]string, 0) + folders = sortFolders(folders) for _, f := range folders { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ @@ -169,8 +125,19 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S Name: f.Title, Data: f, }) + + folderHierarchy[cloudmigration.FolderDataType][f.UID] = f.ParentUID } + // Library Elements + libraryElements, err := s.getLibraryElementsCommands(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get library elements", "err", err) + return nil, err + } + + folderHierarchy[cloudmigration.LibraryElementDataType] = make(map[string]string, 0) + for _, libraryElement := range libraryElements { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.LibraryElementDataType, @@ -178,6 +145,17 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S Name: libraryElement.Name, Data: libraryElement, }) + + if libraryElement.FolderUID != nil { + folderHierarchy[cloudmigration.LibraryElementDataType][libraryElement.UID] = *libraryElement.FolderUID + } + } + + // Alerts: Mute Timings + muteTimings, err := s.getAlertMuteTimings(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert mute timings", "err", err) + return nil, err } for _, muteTiming := range muteTimings { @@ -189,6 +167,13 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Alerts: Notification Templates + notificationTemplates, err := s.getNotificationTemplates(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert notification templates", "err", err) + return nil, err + } + for _, notificationTemplate := range notificationTemplates { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.NotificationTemplateType, @@ -198,6 +183,13 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Alerts: Contact Points + contactPoints, err := s.getContactPoints(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert contact points", "err", err) + return nil, err + } + for _, contactPoint := range contactPoints { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.ContactPointType, @@ -207,6 +199,13 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Alerts: Notification Policies + notificationPolicies, err := s.getNotificationPolicies(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert notification policies", "err", err) + return nil, err + } + if len(notificationPolicies.Name) > 0 { // Notification Policy can only be managed by updating its entire tree, so we send the whole thing as one item. migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ @@ -217,6 +216,13 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Alerts: Alert Rule Groups + alertRuleGroups, err := s.getAlertRuleGroups(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert rule groups", "err", err) + return nil, err + } + for _, alertRuleGroup := range alertRuleGroups { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.AlertRuleGroupType, @@ -226,6 +232,15 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S }) } + // Alerts: Alert Rules + alertRules, err := s.getAlertRules(ctx, signedInUser) + if err != nil { + s.log.Error("Failed to get alert rules", "err", err) + return nil, err + } + + folderHierarchy[cloudmigration.AlertRuleType] = make(map[string]string, 0) + for _, alertRule := range alertRules { migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{ Type: cloudmigration.AlertRuleType, @@ -233,10 +248,12 @@ func (s *Service) getMigrationDataJSON(ctx context.Context, signedInUser *user.S Name: alertRule.Title, Data: alertRule, }) + + folderHierarchy[cloudmigration.AlertRuleType][alertRule.UID] = alertRule.FolderUID } - // Obtain the names of parent elements for Dashboard and Folders data types - parentNamesByType, err := s.getParentNames(ctx, signedInUser, dashs, folders, libraryElements, alertRules) + // Obtain the names of parent elements for data types that have folders. + parentNamesByType, err := s.getParentNames(ctx, signedInUser, folderHierarchy) if err != nil { s.log.Error("Failed to get parent folder names", "err", err) } @@ -805,10 +822,7 @@ func (s *Service) getFolderNamesForFolderUIDs(ctx context.Context, signedInUser func (s *Service) getParentNames( ctx context.Context, signedInUser *user.SignedInUser, - dashboards []dashboards.Dashboard, - folders []folder.CreateFolderCommand, - libraryElements []libraryElement, - alertRules []alertRule, + folderHierarchy map[cloudmigration.MigrateDataType]map[string]string, ) (map[cloudmigration.MigrateDataType]map[string](string), error) { parentNamesByType := make(map[cloudmigration.MigrateDataType]map[string]string) for _, dataType := range currentMigrationTypes { @@ -817,25 +831,18 @@ func (s *Service) getParentNames( // Obtain list of unique folderUIDs parentFolderUIDsSet := make(map[string]struct{}) - for _, dashboard := range dashboards { - // we dont need the root folder - if dashboard.FolderUID != "" { - parentFolderUIDsSet[dashboard.FolderUID] = struct{}{} - } - } - for _, f := range folders { - parentFolderUIDsSet[f.ParentUID] = struct{}{} - } - for _, libraryElement := range libraryElements { - if libraryElement.FolderUID != nil { - parentFolderUIDsSet[*libraryElement.FolderUID] = struct{}{} - } - } - for _, alertRule := range alertRules { - if alertRule.FolderUID != "" { - parentFolderUIDsSet[alertRule.FolderUID] = struct{}{} + + for _, folderUIDs := range folderHierarchy { + for _, folderUID := range folderUIDs { + // Skip the root folder + if folderUID == "" { + continue + } + + parentFolderUIDsSet[folderUID] = struct{}{} } } + parentFolderUIDsSlice := make([]string, 0, len(parentFolderUIDsSet)) for parentFolderUID := range parentFolderUIDsSet { parentFolderUIDsSlice = append(parentFolderUIDsSlice, parentFolderUID) @@ -849,20 +856,9 @@ func (s *Service) getParentNames( } // Prepare map of {data type: {data UID : parentName}} - for _, dashboard := range dashboards { - parentNamesByType[cloudmigration.DashboardDataType][dashboard.UID] = foldersUIDsToFolderName[dashboard.FolderUID] - } - for _, f := range folders { - parentNamesByType[cloudmigration.FolderDataType][f.UID] = foldersUIDsToFolderName[f.ParentUID] - } - for _, libraryElement := range libraryElements { - if libraryElement.FolderUID != nil { - parentNamesByType[cloudmigration.LibraryElementDataType][libraryElement.UID] = foldersUIDsToFolderName[*libraryElement.FolderUID] - } - } - for _, alertRule := range alertRules { - if alertRule.FolderUID != "" { - parentNamesByType[cloudmigration.AlertRuleType][alertRule.UID] = foldersUIDsToFolderName[alertRule.FolderUID] + for dataType, uidFolderMap := range folderHierarchy { + for uid, folderUID := range uidFolderMap { + parentNamesByType[dataType][uid] = foldersUIDsToFolderName[folderUID] } } From 07b520378fcd3efa75f0f38293a8a772e626714f Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:26:17 +0100 Subject: [PATCH 13/86] alerting(ui): update helpUI text for simplified routing (#102225) * alerting(ui): add helpInfo for optional simpleRouting settings * minor copy change * update betterer results * alerting(ui): update helpUI text for simplifiedRouting * fix i18n error with `make i18n-extract` --- .betterer.results | 2 +- .../rule-editor/NotificationsStep.tsx | 14 ++----- .../simplifiedRouting/AlertManagerRouting.tsx | 38 ++++++++++++++++++- .../route-settings/MuteTimingFields.tsx | 11 +++++- public/locales/en-US/grafana.json | 8 ++++ 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/.betterer.results b/.betterer.results index 75f00fb1529..95e276339e3 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1918,7 +1918,7 @@ exports[`better eslint`] = { ], "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] ], "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/contactPoint/ContactPointSelector.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], diff --git a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx index 7d1d429b312..e36e91febb5 100644 --- a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx @@ -270,22 +270,16 @@ function NeedHelpInfoForContactpoint() { <> Select a contact point to notify all recipients in it.
+ Muting, grouping, and timings options allow you to customize how notifications are sent.
- Notifications for firing alert instances are grouped based on folder and alert rule name.
- The wait time before sending the first notification for a new group of alerts is 30 seconds. -
- The waiting time before sending a notification about changes in the alert group after the first notification - has been sent is 5 minutes. -
- The wait time before resending a notification that has already been sent successfully is 4 hours. -
- Grouping and wait time values are defined in your default notification policy. + Alternatively, toggle the Advanced options button to route notifications using notification policies + for greater flexibility. } externalLink="https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/" linkText="Read more about notifications" - title="Notify contact points" + title="Notify by selecting a contact point" /> ); } diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx index de3cb8bfcc8..3d86b3e33cb 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx @@ -3,12 +3,14 @@ import { useEffect, useState } from 'react'; import { useFormContext } from 'react-hook-form'; import { GrafanaTheme2 } from '@grafana/data'; -import { CollapsableSection, Stack, useStyles2 } from '@grafana/ui'; +import { CollapsableSection, Stack, Text, useStyles2 } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form'; import { AlertManagerDataSource } from 'app/features/alerting/unified/utils/datasource'; import { useContactPointsWithStatus } from '../../../contact-points/useContactPoints'; import { ContactPointWithMetadata } from '../../../contact-points/utils'; +import { NeedHelpInfo } from '../../NeedHelpInfo'; import { ContactPointDetails } from './contactPoint/ContactPointDetails'; import { ContactPointSelector } from './contactPoint/ContactPointSelector'; @@ -57,7 +59,7 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
- Alertmanager: + Alertmanager: Alert manager logo {alertManagerName}
@@ -74,8 +76,37 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo label="Muting, grouping and timings (optional)" isOpen={hasRouteSettings} className={styles.collapsableSection} + contentClassName={styles.collapsableSectionContent} > + + + + Configure how notifications for this alert rule are sent. + + + +

+ {t( + 'alerting.rule-form.simple-routing.optional-settings.help-info1', + 'Mute timings allows you to temporarily pause notifications for a specific recurring period, such as a regular maintenance window or weekends.' + )} +

+ {t( + 'alerting.rule-form.simple-routing.optional-settings.help-info2', + 'Grouping and timing options combine multiple alerts within a specific period into a single notification, allowing you to customize default options.' + )} + + } + /> +
@@ -110,6 +141,9 @@ const getStyles = (theme: GrafanaTheme2) => ({ width: 'fit-content', fontSize: theme.typography.body.fontSize, }), + collapsableSectionContent: css({ + padding: '0', + }), routingSection: css({ display: 'flex', flexDirection: 'column', diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx index 86608e72112..0a7810a37a4 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx @@ -1,6 +1,8 @@ +import { css } from '@emotion/css'; import { Controller, useFormContext } from 'react-hook-form'; -import { Field } from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { Field, useStyles2 } from '@grafana/ui'; import MuteTimingsSelector from 'app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector'; import { BaseAlertmanagerArgs } from 'app/features/alerting/unified/types/hooks'; import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form'; @@ -8,6 +10,7 @@ import { mapMultiSelectValueToStrings } from 'app/features/alerting/unified/util /** Provides a form field for use in simplified routing, for selecting appropriate mute timings */ export function MuteTimingFields({ alertmanager }: BaseAlertmanagerArgs) { + const styles = useStyles2(getStyles); const { control, formState: { errors }, @@ -18,6 +21,7 @@ export function MuteTimingFields({ alertmanager }: BaseAlertmanagerArgs) { label="Mute timings" data-testid="am-mute-timing-select" description="Select a mute timing to define when not to send notifications for this alert rule" + className={styles.muteTimingField} invalid={!!errors.contactPoints?.[alertmanager]?.muteTimeIntervals} > ); } +const getStyles = (theme: GrafanaTheme2) => ({ + muteTimingField: css({ + marginTop: theme.spacing(1), + }), +}); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 0f8c5c3fac9..48e03c5a9d6 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -730,6 +730,14 @@ "pause": { "label": "Pause evaluation" }, + "simple-routing": { + "alertmanager-label": "Alertmanager:", + "optional-settings": { + "description": "Configure how notifications for this alert rule are sent.", + "help-info1": "Mute timings allows you to temporarily pause notifications for a specific recurring period, such as a regular maintenance window or weekends.", + "help-info2": "Grouping and timing options combine multiple alerts within a specific period into a single notification, allowing you to customize default options." + } + }, "threshold": { "recovery": { "stop-alerting-above": "Stop alerting when above", From cb532cafeff923fb788b728466d61c479f82be1a Mon Sep 17 00:00:00 2001 From: linoman <2051016+linoman@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:28:51 +0100 Subject: [PATCH 14/86] SCIM: Validate provisioned users (#102099) * Validate authID when user is provisioned * Add new `user_unique_id` to `user_auth` table * Validate provisioned user with saml assertion * Rework `ExternalUID` * Validate for ExternalUID only * Enhance verbosity * Move ExternalUID to saml config * Rename db variable for externalUID * Add verbosity to debug ExternalUID * Assign new error for ExternalUID mismatch * Add `GetByLoginFn` * Add new configuration to saml tests * add validation for empty externalUID --- .../authn/authnimpl/sync/user_sync.go | 41 +++++++++- pkg/services/authn/identity.go | 2 + pkg/services/login/authinfoimpl/store.go | 9 ++- pkg/services/login/model.go | 10 ++- .../sqlstore/migrations/user_auth_mig.go | 4 + .../ssosettings/strategies/saml_strategy.go | 75 ++++++++++--------- .../strategies/saml_strategy_test.go | 75 ++++++++++--------- pkg/services/user/usertest/fake.go | 4 + 8 files changed, 135 insertions(+), 85 deletions(-) diff --git a/pkg/services/authn/authnimpl/sync/user_sync.go b/pkg/services/authn/authnimpl/sync/user_sync.go index f1ccefaae48..2f55c3a9cac 100644 --- a/pkg/services/authn/authnimpl/sync/user_sync.go +++ b/pkg/services/authn/authnimpl/sync/user_sync.go @@ -7,6 +7,7 @@ import ( "strconv" claims "github.com/grafana/authlib/types" + "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/singleflight" "github.com/grafana/grafana/pkg/apimachinery/errutil" @@ -45,6 +46,14 @@ var ( "user.sync.fetch-not-found", errutil.WithPublicMessage("User not found"), ) + errMismatchedExternalUID = errutil.Unauthorized( + "user.sync.mismatched-externalUID", + errutil.WithPublicMessage("Mismatched externalUID"), + ) + errEmptyExternalUID = errutil.Unauthorized( + "user.sync.empty-externalUID", + errutil.WithPublicMessage("Empty externalUID"), + ) ) var ( @@ -231,7 +240,7 @@ func (s *UserSync) upsertAuthConnection(ctx context.Context, userID int64, ident return nil } - // If a user does not a connection to a specific auth module, create it. + // If a user does not have a connection to a specific auth module, create it. // This can happen when: using multiple auth client where the same user exists in several or // changing to new auth client if createConnection { @@ -265,6 +274,8 @@ func (s *UserSync) updateUserAttributes(ctx context.Context, usr *user.User, id ctx, span := s.tracer.Start(ctx, "user.sync.updateUserAttributes") defer span.End() + needsConnectionCreation := userAuth == nil + if errProtection := s.userProtectionService.AllowUserMapping(usr, id.AuthenticatedBy); errProtection != nil { return errUserProtection.Errorf("user mapping not allowed: %w", errProtection) } @@ -305,14 +316,38 @@ func (s *UserSync) updateUserAttributes(ctx context.Context, usr *user.User, id needsUpdate = true } - if needsUpdate { + span.SetAttributes( + attribute.String("identity.ID", id.ID), + attribute.String("identity.ExternalUID", id.ExternalUID), + ) + if usr.IsProvisioned { + s.log.Debug("User is provisioned", "id,UID", id.UID) + needsConnectionCreation = false + authInfo, err := s.authInfoService.GetAuthInfo(ctx, &login.GetAuthInfoQuery{UserId: usr.ID, AuthModule: id.AuthenticatedBy}) + if err != nil { + s.log.Error("Error getting auth info", "error", err) + return err + } + + if id.ExternalUID == "" { + s.log.Error("externalUID is empty", "id", id.UID) + return errEmptyExternalUID.Errorf("externalUID is empty") + } + + if id.ExternalUID != authInfo.ExternalUID { + s.log.Error("mismatched externalUID", "provisioned_externalUID", authInfo.ExternalUID, "identity_externalUID", id.ExternalUID) + return errMismatchedExternalUID.Errorf("externalUID mistmatch") + } + } + + if needsUpdate && !usr.IsProvisioned { s.log.FromContext(ctx).Debug("Syncing user info", "id", id.ID, "update", fmt.Sprintf("%v", updateCmd)) if err := s.userService.Update(ctx, updateCmd); err != nil { return err } } - return s.upsertAuthConnection(ctx, usr.ID, id, userAuth == nil) + return s.upsertAuthConnection(ctx, usr.ID, id, needsConnectionCreation) } func (s *UserSync) createUser(ctx context.Context, id *authn.Identity) (*user.User, error) { diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index fd971e7070f..2b6e7bfe533 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -76,6 +76,8 @@ type Identity struct { Permissions map[int64]map[string][]string // IDToken is a signed token representing the identity that can be forwarded to plugins and external services. IDToken string + // ExternalUID is the unique identifier for the entity in the external system. + ExternalUID string IDTokenClaims *authn.Claims[authn.IDTokenClaims] AccessTokenClaims *authn.Claims[authn.AccessTokenClaims] diff --git a/pkg/services/login/authinfoimpl/store.go b/pkg/services/login/authinfoimpl/store.go index b5a90fc2a8a..84c405bf276 100644 --- a/pkg/services/login/authinfoimpl/store.go +++ b/pkg/services/login/authinfoimpl/store.go @@ -107,10 +107,11 @@ func (s *Store) GetUserLabels(ctx context.Context, query login.GetUserLabelsQuer func (s *Store) SetAuthInfo(ctx context.Context, cmd *login.SetAuthInfoCommand) error { authUser := &login.UserAuth{ - UserId: cmd.UserId, - AuthModule: cmd.AuthModule, - AuthId: cmd.AuthId, - Created: GetTime(), + UserId: cmd.UserId, + AuthModule: cmd.AuthModule, + AuthId: cmd.AuthId, + ExternalUID: cmd.ExternalUID, + Created: GetTime(), } if cmd.OAuthToken != nil { diff --git a/pkg/services/login/model.go b/pkg/services/login/model.go index 3d755a80301..979a89d6fb2 100644 --- a/pkg/services/login/model.go +++ b/pkg/services/login/model.go @@ -23,6 +23,7 @@ type UserAuth struct { OAuthIdToken string OAuthTokenType string OAuthExpiry time.Time + ExternalUID string `xorm:"external_uid"` } type ExternalUserInfo struct { @@ -72,10 +73,11 @@ type RequestURIKey struct{} // COMMANDS type SetAuthInfoCommand struct { - AuthModule string - AuthId string - UserId int64 - OAuthToken *oauth2.Token + AuthModule string + AuthId string + UserId int64 + OAuthToken *oauth2.Token + ExternalUID string } type UpdateAuthInfoCommand struct { diff --git a/pkg/services/sqlstore/migrations/user_auth_mig.go b/pkg/services/sqlstore/migrations/user_auth_mig.go index bf3b98955b7..9546d84a766 100644 --- a/pkg/services/sqlstore/migrations/user_auth_mig.go +++ b/pkg/services/sqlstore/migrations/user_auth_mig.go @@ -46,4 +46,8 @@ func addUserAuthMigrations(mg *Migrator) { mg.AddMigration("Add OAuth ID token to user_auth", NewAddColumnMigration(userAuthV1, &Column{ Name: "o_auth_id_token", Type: DB_Text, Nullable: true, })) + + mg.AddMigration("Add user_unique_id to user_auth", NewAddColumnMigration(userAuthV1, &Column{ + Name: "external_uid", Type: DB_Text, Nullable: true, + })) } diff --git a/pkg/services/ssosettings/strategies/saml_strategy.go b/pkg/services/ssosettings/strategies/saml_strategy.go index 026fd5cef7f..a863b9f491e 100644 --- a/pkg/services/ssosettings/strategies/saml_strategy.go +++ b/pkg/services/ssosettings/strategies/saml_strategy.go @@ -32,43 +32,44 @@ func (s *SAMLStrategy) GetProviderConfig(_ context.Context, provider string) (ma func (s *SAMLStrategy) loadSAMLSettings() map[string]any { section := s.settingsProvider.Section("auth.saml") result := map[string]any{ - "enabled": section.KeyValue("enabled").MustBool(false), - "entity_id": section.KeyValue("entity_id").MustString(""), - "name": section.KeyValue("name").MustString("SAML"), - "single_logout": section.KeyValue("single_logout").MustBool(false), - "allow_sign_up": section.KeyValue("allow_sign_up").MustBool(false), - "auto_login": section.KeyValue("auto_login").MustBool(false), - "certificate": section.KeyValue("certificate").MustString(""), - "certificate_path": section.KeyValue("certificate_path").MustString(""), - "private_key": section.KeyValue("private_key").MustString(""), - "private_key_path": section.KeyValue("private_key_path").MustString(""), - "signature_algorithm": section.KeyValue("signature_algorithm").MustString(""), - "idp_metadata": section.KeyValue("idp_metadata").MustString(""), - "idp_metadata_path": section.KeyValue("idp_metadata_path").MustString(""), - "idp_metadata_url": section.KeyValue("idp_metadata_url").MustString(""), - "max_issue_delay": section.KeyValue("max_issue_delay").MustDuration(90 * time.Second), - "metadata_valid_duration": section.KeyValue("metadata_valid_duration").MustDuration(48 * time.Hour), - "allow_idp_initiated": section.KeyValue("allow_idp_initiated").MustBool(false), - "relay_state": section.KeyValue("relay_state").MustString(""), - "assertion_attribute_name": section.KeyValue("assertion_attribute_name").MustString(""), - "assertion_attribute_login": section.KeyValue("assertion_attribute_login").MustString(""), - "assertion_attribute_email": section.KeyValue("assertion_attribute_email").MustString(""), - "assertion_attribute_groups": section.KeyValue("assertion_attribute_groups").MustString(""), - "assertion_attribute_role": section.KeyValue("assertion_attribute_role").MustString(""), - "assertion_attribute_org": section.KeyValue("assertion_attribute_org").MustString(""), - "allowed_organizations": section.KeyValue("allowed_organizations").MustString(""), - "org_mapping": section.KeyValue("org_mapping").MustString(""), - "role_values_none": section.KeyValue("role_values_none").MustString(""), - "role_values_viewer": section.KeyValue("role_values_viewer").MustString(""), - "role_values_editor": section.KeyValue("role_values_editor").MustString(""), - "role_values_admin": section.KeyValue("role_values_admin").MustString(""), - "role_values_grafana_admin": section.KeyValue("role_values_grafana_admin").MustString(""), - "name_id_format": section.KeyValue("name_id_format").MustString(""), - "skip_org_role_sync": section.KeyValue("skip_org_role_sync").MustBool(false), - "client_id": section.KeyValue("client_id").MustString(""), - "client_secret": section.KeyValue("client_secret").MustString(""), - "token_url": section.KeyValue("token_url").MustString(""), - "force_use_graph_api": section.KeyValue("force_use_graph_api").MustBool(false), + "allow_idp_initiated": section.KeyValue("allow_idp_initiated").MustBool(false), + "allow_sign_up": section.KeyValue("allow_sign_up").MustBool(false), + "allowed_organizations": section.KeyValue("allowed_organizations").MustString(""), + "assertion_attribute_email": section.KeyValue("assertion_attribute_email").MustString(""), + "assertion_attribute_groups": section.KeyValue("assertion_attribute_groups").MustString(""), + "assertion_attribute_login": section.KeyValue("assertion_attribute_login").MustString(""), + "assertion_attribute_name": section.KeyValue("assertion_attribute_name").MustString(""), + "assertion_attribute_org": section.KeyValue("assertion_attribute_org").MustString(""), + "assertion_attribute_role": section.KeyValue("assertion_attribute_role").MustString(""), + "auto_login": section.KeyValue("auto_login").MustBool(false), + "certificate": section.KeyValue("certificate").MustString(""), + "certificate_path": section.KeyValue("certificate_path").MustString(""), + "client_id": section.KeyValue("client_id").MustString(""), + "client_secret": section.KeyValue("client_secret").MustString(""), + "enabled": section.KeyValue("enabled").MustBool(false), + "entity_id": section.KeyValue("entity_id").MustString(""), + "external_uid_assertion_name": section.KeyValue("external_uid_assertion_name").MustString(""), + "force_use_graph_api": section.KeyValue("force_use_graph_api").MustBool(false), + "idp_metadata": section.KeyValue("idp_metadata").MustString(""), + "idp_metadata_path": section.KeyValue("idp_metadata_path").MustString(""), + "idp_metadata_url": section.KeyValue("idp_metadata_url").MustString(""), + "max_issue_delay": section.KeyValue("max_issue_delay").MustDuration(90 * time.Second), + "metadata_valid_duration": section.KeyValue("metadata_valid_duration").MustDuration(48 * time.Hour), + "name": section.KeyValue("name").MustString("SAML"), + "name_id_format": section.KeyValue("name_id_format").MustString(""), + "org_mapping": section.KeyValue("org_mapping").MustString(""), + "private_key": section.KeyValue("private_key").MustString(""), + "private_key_path": section.KeyValue("private_key_path").MustString(""), + "relay_state": section.KeyValue("relay_state").MustString(""), + "role_values_admin": section.KeyValue("role_values_admin").MustString(""), + "role_values_editor": section.KeyValue("role_values_editor").MustString(""), + "role_values_grafana_admin": section.KeyValue("role_values_grafana_admin").MustString(""), + "role_values_none": section.KeyValue("role_values_none").MustString(""), + "role_values_viewer": section.KeyValue("role_values_viewer").MustString(""), + "signature_algorithm": section.KeyValue("signature_algorithm").MustString(""), + "single_logout": section.KeyValue("single_logout").MustBool(false), + "skip_org_role_sync": section.KeyValue("skip_org_role_sync").MustBool(false), + "token_url": section.KeyValue("token_url").MustString(""), } return result } diff --git a/pkg/services/ssosettings/strategies/saml_strategy_test.go b/pkg/services/ssosettings/strategies/saml_strategy_test.go index c04ef280318..507005a45b2 100644 --- a/pkg/services/ssosettings/strategies/saml_strategy_test.go +++ b/pkg/services/ssosettings/strategies/saml_strategy_test.go @@ -54,43 +54,44 @@ var ( ` expectedSAMLInfo = map[string]any{ - "enabled": true, - "entity_id": "custom-entity-id", - "single_logout": true, - "allow_sign_up": true, - "auto_login": true, - "name": "SAML Test", - "certificate": "devenv/docker/blocks/auth/saml-enterprise/cert.crt", - "certificate_path": "/path/to/cert", - "private_key": "dGhpcyBpcyBteSBwcml2YXRlIGtleSB0aGF0IEkgd2FudCB0byBnZXQgZW5jb2RlZCBpbiBiYXNlIDY0", - "private_key_path": "devenv/docker/blocks/auth/saml-enterprise/key.pem", - "signature_algorithm": "rsa-sha256", - "idp_metadata": "dGhpcyBpcyBteSBwcml2YXRlIGtleSB0aGF0IEkgd2FudCB0byBnZXQgZW5jb2RlZCBpbiBiYXNlIDY0", - "idp_metadata_path": "/path/to/metadata", - "idp_metadata_url": "http://localhost:8086/realms/grafana/protocol/saml/descriptor", - "max_issue_delay": 90 * time.Second, - "metadata_valid_duration": 48 * time.Hour, - "allow_idp_initiated": false, - "relay_state": "relay_state", - "assertion_attribute_name": "name", - "assertion_attribute_login": "login", - "assertion_attribute_email": "email", - "assertion_attribute_groups": "groups", - "assertion_attribute_role": "roles", - "assertion_attribute_org": "orgs", - "allowed_organizations": "org1 org2", - "org_mapping": "org1:1:editor, *:2:viewer", - "role_values_viewer": "viewer", - "role_values_editor": "editor", - "role_values_admin": "admin", - "role_values_grafana_admin": "serveradmin", - "name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - "skip_org_role_sync": false, - "role_values_none": "guest disabled", - "token_url": "http://localhost:8086/auth/realms/grafana/protocol/openid-connect/token", - "client_id": "grafana", - "client_secret": "grafana", - "force_use_graph_api": false, + "enabled": true, + "entity_id": "custom-entity-id", + "external_uid_assertion_name": "", + "single_logout": true, + "allow_sign_up": true, + "auto_login": true, + "name": "SAML Test", + "certificate": "devenv/docker/blocks/auth/saml-enterprise/cert.crt", + "certificate_path": "/path/to/cert", + "private_key": "dGhpcyBpcyBteSBwcml2YXRlIGtleSB0aGF0IEkgd2FudCB0byBnZXQgZW5jb2RlZCBpbiBiYXNlIDY0", + "private_key_path": "devenv/docker/blocks/auth/saml-enterprise/key.pem", + "signature_algorithm": "rsa-sha256", + "idp_metadata": "dGhpcyBpcyBteSBwcml2YXRlIGtleSB0aGF0IEkgd2FudCB0byBnZXQgZW5jb2RlZCBpbiBiYXNlIDY0", + "idp_metadata_path": "/path/to/metadata", + "idp_metadata_url": "http://localhost:8086/realms/grafana/protocol/saml/descriptor", + "max_issue_delay": 90 * time.Second, + "metadata_valid_duration": 48 * time.Hour, + "allow_idp_initiated": false, + "relay_state": "relay_state", + "assertion_attribute_name": "name", + "assertion_attribute_login": "login", + "assertion_attribute_email": "email", + "assertion_attribute_groups": "groups", + "assertion_attribute_role": "roles", + "assertion_attribute_org": "orgs", + "allowed_organizations": "org1 org2", + "org_mapping": "org1:1:editor, *:2:viewer", + "role_values_viewer": "viewer", + "role_values_editor": "editor", + "role_values_admin": "admin", + "role_values_grafana_admin": "serveradmin", + "name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "skip_org_role_sync": false, + "role_values_none": "guest disabled", + "token_url": "http://localhost:8086/auth/realms/grafana/protocol/openid-connect/token", + "client_id": "grafana", + "client_secret": "grafana", + "force_use_graph_api": false, } ) diff --git a/pkg/services/user/usertest/fake.go b/pkg/services/user/usertest/fake.go index 09a456d81d5..d0552d16d0f 100644 --- a/pkg/services/user/usertest/fake.go +++ b/pkg/services/user/usertest/fake.go @@ -20,6 +20,7 @@ type FakeUserService struct { UpdateFn func(ctx context.Context, cmd *user.UpdateUserCommand) error GetSignedInUserFn func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) CreateFn func(ctx context.Context, cmd *user.CreateUserCommand) (*user.User, error) + GetByLoginFn func(ctx context.Context, query *user.GetUserByLoginQuery) (*user.User, error) BatchDisableUsersFn func(ctx context.Context, cmd *user.BatchDisableUsersCommand) error GetByEmailFn func(ctx context.Context, query *user.GetUserByEmailQuery) (*user.User, error) @@ -59,6 +60,9 @@ func (f *FakeUserService) GetByUID(ctx context.Context, query *user.GetUserByUID } func (f *FakeUserService) GetByLogin(ctx context.Context, query *user.GetUserByLoginQuery) (*user.User, error) { + if f.GetByLoginFn != nil { + return f.GetByLoginFn(ctx, query) + } return f.ExpectedUser, f.ExpectedError } From dfe2af9559eafc8942ad2d87a110ee3c14a415d9 Mon Sep 17 00:00:00 2001 From: Joey <90795735+joey-grafana@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:17:32 +0000 Subject: [PATCH 15/86] Tempo: Remove aggregate by (#98474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove aggregate by * Update betterer * Run toggles test * Add back group by in schema so it can be checked for * Show error if group by * Update error message * Allow user to remove the group by from the query * Fix assertion --------- Co-authored-by: Piotr Jamróz --- .betterer.results | 3 - .../feature-toggles/index.md | 1 - .../src/types/featureToggles.gen.ts | 4 - .../dataquery/x/TempoDataQuery_types.gen.ts | 2 +- pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../kinds/dataquery/types_dataquery_gen.go | 2 +- .../SearchTraceQLEditor/AggregateByAlert.tsx | 20 ++ .../SearchTraceQLEditor/GroupByField.test.tsx | 162 ---------- .../SearchTraceQLEditor/GroupByField.tsx | 193 ----------- .../TraceQLSearch.test.tsx | 28 +- .../SearchTraceQLEditor/TraceQLSearch.tsx | 20 +- .../plugins/datasource/tempo/dataquery.cue | 2 +- .../plugins/datasource/tempo/dataquery.gen.ts | 2 +- .../datasource/tempo/datasource.test.ts | 26 -- .../plugins/datasource/tempo/datasource.ts | 138 +------- .../tempo/language_provider.test.ts | 32 -- .../datasource/tempo/language_provider.ts | 12 - .../datasource/tempo/metricsSummary.test.ts | 300 ------------------ .../datasource/tempo/metricsSummary.ts | 273 ---------------- public/app/plugins/datasource/tempo/utils.ts | 1 - 23 files changed, 61 insertions(+), 1175 deletions(-) create mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx delete mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx delete mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx delete mode 100644 public/app/plugins/datasource/tempo/metricsSummary.test.ts delete mode 100644 public/app/plugins/datasource/tempo/metricsSummary.ts diff --git a/.betterer.results b/.betterer.results index 95e276339e3..053ef6bfd02 100644 --- a/.betterer.results +++ b/.betterer.results @@ -6557,9 +6557,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/tempo/SearchTraceQLEditor/DurationInput.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], - "public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], 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 bf962a6c350..84bc6c9d3b3 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -150,7 +150,6 @@ Experimental features might be changed or removed without prior notice. | `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor | | `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | | `mlExpressions` | Enable support for Machine Learning in server-side expressions | -| `metricsSummary` | Enables metrics summary queries in the Tempo data source | | `datasourceAPIServers` | Expose some datasources as apiservers. | | `provisioning` | Next generation provisioning... and git | | `permissionsFilterRemoveSubquery` | Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 40dc24a523d..46f4da08a67 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -271,10 +271,6 @@ export interface FeatureToggles { */ traceQLStreaming?: boolean; /** - * Enables metrics summary queries in the Tempo data source - */ - metricsSummary?: boolean; - /** * Expose some datasources as apiservers. */ datasourceAPIServers?: boolean; diff --git a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts index 9a279f1653b..342f399cd17 100644 --- a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts @@ -19,7 +19,7 @@ export interface TempoQuery extends common.DataQuery { exemplars?: number; filters: Array; /** - * Filters that are used to query the metrics summary + * deprecated Filters that are used to query the metrics summary */ groupBy?: Array; /** diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 320342ff7e2..89f2d6f8220 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -441,13 +441,6 @@ var ( Owner: grafanaObservabilityTracesAndProfilingSquad, Expression: "false", }, - { - Name: "metricsSummary", - Description: "Enables metrics summary queries in the Tempo data source", - Stage: FeatureStageExperimental, - FrontendOnly: true, - Owner: grafanaObservabilityTracesAndProfilingSquad, - }, { Name: "datasourceAPIServers", Description: "Expose some datasources as apiservers.", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index b86059bd2be..62bd4844236 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -58,7 +58,6 @@ awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,false,false, transformationsRedesign,GA,@grafana/observability-metrics,false,false,true mlExpressions,experimental,@grafana/alerting-squad,false,false,false traceQLStreaming,GA,@grafana/observability-traces-and-profiling,false,false,true -metricsSummary,experimental,@grafana/observability-traces-and-profiling,false,false,true datasourceAPIServers,experimental,@grafana/grafana-app-platform-squad,false,true,false grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false provisioning,experimental,@grafana/grafana-app-platform-squad,false,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 7207be7a8a2..b1d5e756e5b 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -243,10 +243,6 @@ const ( // Enables response streaming of TraceQL queries of the Tempo data source FlagTraceQLStreaming = "traceQLStreaming" - // FlagMetricsSummary - // Enables metrics summary queries in the Tempo data source - FlagMetricsSummary = "metricsSummary" - // FlagDatasourceAPIServers // Expose some datasources as apiservers. FlagDatasourceAPIServers = "datasourceAPIServers" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 3f65b33ffc0..1d0e357c378 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -2768,7 +2768,8 @@ "metadata": { "name": "metricsSummary", "resourceVersion": "1718727528075", - "creationTimestamp": "2023-08-28T14:02:12Z" + "creationTimestamp": "2023-08-28T14:02:12Z", + "deletionTimestamp": "2024-11-25T10:47:18Z" }, "spec": { "description": "Enables metrics summary queries in the Tempo data source", diff --git a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go index 874c4161741..457ed0c9baa 100644 --- a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go @@ -48,7 +48,7 @@ type TempoQuery struct { // Defines the maximum number of spans per spanset that are returned from Tempo Spss *int64 `json:"spss,omitempty"` Filters []TraceqlFilter `json:"filters"` - // Filters that are used to query the metrics summary + // deprecated Filters that are used to query the metrics summary GroupBy []TraceqlFilter `json:"groupBy,omitempty"` // The type of the table that is used to display the search results TableType *SearchTableType `json:"tableType,omitempty"` diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx new file mode 100644 index 00000000000..ccb979569df --- /dev/null +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import { Alert, Button } from '@grafana/ui'; + +import { TempoQuery } from '../dataquery.gen'; + +export function AggregateByAlert({ + query, + onChange, +}: { + query: TempoQuery; + onChange?: () => void; +}): React.ReactNode | null { + return query.groupBy ? ( + + The aggregate by feature has been removed. We recommend using Traces Drildown app instead.   + + + ) : null; +} diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx deleted file mode 100644 index 8a35d0905c5..00000000000 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { useState } from 'react'; - -import { TraceqlSearchScope } from '../dataquery.gen'; -import { TempoDatasource } from '../datasource'; -import TempoLanguageProvider from '../language_provider'; -import { initTemplateSrv } from '../test/test_utils'; -import { TempoQuery } from '../types'; - -import { GroupByField } from './GroupByField'; - -describe('GroupByField', () => { - let user: ReturnType; - - const datasource: TempoDatasource = { - search: { - filters: [], - }, - } as unknown as TempoDatasource; - - const lp = new TempoLanguageProvider(datasource); - datasource.languageProvider = lp; - - let query: TempoQuery = { - refId: 'A', - queryType: 'traceqlSearch', - query: '', - filters: [], - groupBy: [{ id: 'group-by-id', scope: TraceqlSearchScope.Span, tag: 'component' }], - }; - - const onChange = (q: TempoQuery) => { - query = q; - }; - - jest.spyOn(lp, 'getMetricsSummaryTags').mockReturnValue(['component', 'http.method', 'http.status_code']); - jest.spyOn(lp, 'getTags').mockReturnValue(['component', 'http.method', 'http.status_code']); - - beforeEach(() => { - jest.useFakeTimers(); - // Need to use delay: null here to work with fakeTimers - // see https://github.com/testing-library/user-event/issues/833 - user = userEvent.setup({ delay: null }); - - initTemplateSrv([{ name: 'templateVariable1' }, { name: 'templateVariable2' }], {}); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should only show add/remove tag when necessary', async () => { - const GroupByWithProps = () => { - const [query, setQuery] = useState({ - refId: 'A', - queryType: 'traceqlSearch', - key: 'Q-595a9bbc-2a25-49a7-9249-a52a0a475d83-0', - filters: [], - groupBy: [{ id: 'group-by-id', scope: TraceqlSearchScope.Span }], - }); - return ( - setQuery(q)} - isTagsLoading={false} - /> - ); - }; - render(); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the default tag, so no need to add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(0); // mot filled in the default tag, so no values to remove - expect(screen.getAllByText('Select tag').length).toBe(1); - - await user.click(screen.getByText('Select tag')); - jest.advanceTimersByTime(1000); - await user.click(screen.getByText('http.method')); - jest.advanceTimersByTime(1000); - expect(screen.getAllByLabelText('Add tag').length).toBe(1); - expect(screen.getAllByLabelText(/Remove tag/).length).toBe(1); - - await user.click(screen.getByLabelText('Add tag')); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the new tag, so no need to add another one - expect(screen.getAllByLabelText(/Remove tag/).length).toBe(2); // one for each tag - - await user.click(screen.getAllByLabelText(/Remove tag/)[1]); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(1); // filled in the default tag, so can add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(1); // filled in the default tag, so can remove values - - await user.click(screen.getAllByLabelText(/Remove tag/)[0]); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the default tag, so no need to add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(0); // mot filled in the default tag, so no values to remove - }); - - it('should update scope when new value is selected in scope input', async () => { - const { container } = render( - - ); - - const scopeSelect = container.querySelector(`input[aria-label="Select scope for filter 1"]`); - expect(scopeSelect).not.toBeNull(); - expect(scopeSelect).toBeInTheDocument(); - - if (scopeSelect) { - await user.click(scopeSelect); - jest.advanceTimersByTime(1000); - const resourceScope = await screen.findByText('resource'); - await user.click(resourceScope); - const groupByFilter = query.groupBy?.find((f) => f.id === 'group-by-id'); - expect(groupByFilter).not.toBeNull(); - expect(groupByFilter?.scope).toBe('resource'); - expect(groupByFilter?.tag).toBe(''); - } - }); - - it('should update tag when new value is selected in tag input', async () => { - const { container } = render( - - ); - - const tagSelect = container.querySelector(`input[aria-label="Select tag for filter 1"]`); - expect(tagSelect).not.toBeNull(); - expect(tagSelect).toBeInTheDocument(); - - if (tagSelect) { - await user.click(tagSelect); - jest.advanceTimersByTime(1000); - const tag = await screen.findByText('http.method'); - await user.click(tag); - const groupByFilter = query.groupBy?.find((f) => f.id === 'group-by-id'); - expect(groupByFilter).not.toBeNull(); - expect(groupByFilter?.tag).toBe('http.method'); - } - }); - - it('should allow selecting template variables', async () => { - const { container } = render( - - ); - - const tagSelect = container.querySelector(`input[aria-label="Select tag for filter 1"]`); - expect(tagSelect).not.toBeNull(); - expect(tagSelect).toBeInTheDocument(); - - if (tagSelect) { - await user.click(tagSelect); - jest.advanceTimersByTime(1000); - expect(await screen.findByText('$templateVariable1')).toBeInTheDocument(); - expect(await screen.findByText('$templateVariable2')).toBeInTheDocument(); - } - }); -}); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx deleted file mode 100644 index 14134fec7ec..00000000000 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { css } from '@emotion/css'; -import { useEffect, useMemo, useState } from 'react'; -import { v4 as uuidv4 } from 'uuid'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { AccessoryButton } from '@grafana/plugin-ui'; -import { Alert, HorizontalGroup, InputActionMeta, Select, useStyles2 } from '@grafana/ui'; - -import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; -import { TempoDatasource } from '../datasource'; -import { OPTIONS_LIMIT } from '../language_provider'; -import { TempoQuery } from '../types'; - -import InlineSearchField from './InlineSearchField'; -import { withTemplateVariableOptions } from './SearchField'; -import { replaceAt } from './utils'; - -interface Props { - datasource: TempoDatasource; - onChange: (value: TempoQuery) => void; - query: Partial & TempoQuery; - isTagsLoading: boolean; - addVariablesToOptions?: boolean; -} - -export const GroupByField = (props: Props) => { - const { datasource, onChange, query, isTagsLoading, addVariablesToOptions } = props; - const styles = useStyles2(getStyles); - const generateId = () => uuidv4().slice(0, 8); - const [tagQuery, setTagQuery] = useState(''); - - useEffect(() => { - if (!query.groupBy || query.groupBy.length === 0) { - onChange({ - ...query, - groupBy: [ - { - id: generateId(), - scope: TraceqlSearchScope.Span, - }, - ], - }); - } - }, [onChange, query]); - - const tagOptions = useMemo( - () => (f: TraceqlFilter) => { - const tags = datasource!.languageProvider.getMetricsSummaryTags(f.scope); - if (tagQuery.length === 0) { - return tags.slice(0, OPTIONS_LIMIT); - } - - const queryLowerCase = tagQuery.toLowerCase(); - return tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, OPTIONS_LIMIT); - }, - [datasource, tagQuery] - ); - - const addFilter = () => { - updateFilter({ - id: generateId(), - scope: TraceqlSearchScope.Span, - }); - }; - - const removeFilter = (filter: TraceqlFilter) => { - onChange({ ...query, groupBy: query.groupBy?.filter((f) => f.id !== filter.id) }); - }; - - const updateFilter = (filter: TraceqlFilter) => { - const copy = { ...query }; - copy.groupBy ||= []; - const indexOfFilter = copy.groupBy.findIndex((f) => f.id === filter.id); - if (indexOfFilter >= 0) { - copy.groupBy = replaceAt(copy.groupBy, indexOfFilter, filter); - } else { - copy.groupBy.push(filter); - } - onChange(copy); - }; - - const scopeOptions = Object.values(TraceqlSearchScope) - .filter((s) => { - // only add scope if it has tags - return datasource.languageProvider.getTags(s).length > 0; - }) - .map((t) => ({ label: t, value: t })); - - return ( - - <> - {query.groupBy?.map((f, i) => { - const tags = tagOptions(f) - ?.concat(f.tag !== undefined && f.tag !== '' && !tagOptions(f)?.includes(f.tag) ? [f.tag] : []) - .map((t) => ({ - label: t, - value: t, - })); - return ( -
- - { - updateFilter({ ...f, tag: v?.value }); - }} - options={addVariablesToOptions ? withTemplateVariableOptions(tags) : tags} - onInputChange={(value: string, { action }: InputActionMeta) => { - if (action === 'input-change') { - setTagQuery(value); - } - }} - onCloseMenu={() => setTagQuery('')} - placeholder="Select tag" - value={f.tag || ''} - /> - {(f.tag || (query.groupBy?.length ?? 0) > 1) && ( - removeFilter(f)} - tooltip="Remove tag" - title={`Remove tag for filter ${i + 1}`} - variant="secondary" - /> - )} - {f.tag && i === (query.groupBy?.length ?? 0) - 1 && ( - - addFilter()} - tooltip="Add tag" - variant="secondary" - /> - - )} - -
- ); - })} - {query.groupBy && query.groupBy.length > 0 && query.groupBy[0].tag && ( - - The aggregate by feature is deprecated. We recommend using Explore Traces instead. If you want to write your - own TraceQL queries to replicate this API, please check - - this page - - . - - )} - -
- ); -}; - -const getStyles = (theme: GrafanaTheme2) => ({ - addTag: css({ - marginLeft: theme.spacing(1), - }), - notice: css({ - width: '500px', - marginTop: theme.spacing(0.75), - }), - noticeLink: css({ - color: theme.colors.text.link, - textDecoration: 'underline', - marginLeft: '5px', - }), -}); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx index 69ee73631f7..5957dfd90d2 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx @@ -2,8 +2,6 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useState } from 'react'; -import { config } from '@grafana/runtime'; - import { TraceqlSearchScope } from '../dataquery.gen'; import { TempoDatasource } from '../datasource'; import TempoLanguageProvider from '../language_provider'; @@ -238,26 +236,28 @@ describe('TraceQLSearch', () => { }); }); - it('should not render group by when feature toggle is not enabled', async () => { - await waitFor(() => { + it('should not render group by alert when query does not contain group by', async () => { + await act(async () => { render( ); - const groupBy = screen.queryByText('Aggregate by'); - expect(groupBy).toBeNull(); - expect(groupBy).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Remove aggregate by from this query' })).not.toBeInTheDocument(); }); }); - it('should render group by when feature toggle enabled', async () => { - config.featureToggles.metricsSummary = true; - await waitFor(() => { + it('should render group by alert when query contains group by', async () => { + const onChange = jest.fn(); + await waitFor(async () => { render( - + ); - const groupBy = screen.queryByText('Aggregate by'); - expect(groupBy).not.toBeNull(); - expect(groupBy).toBeInTheDocument(); + const button = screen.queryByRole('button', { name: 'Remove aggregate by from this query' }); + expect(button).toBeInTheDocument(); }); }); }); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx index 7ef9bd88303..fee68d5c771 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx @@ -13,8 +13,8 @@ import { TempoQueryBuilderOptions } from '../traceql/TempoQueryBuilderOptions'; import { traceqlGrammar } from '../traceql/traceql'; import { TempoQuery } from '../types'; +import { AggregateByAlert } from './AggregateByAlert'; import DurationInput from './DurationInput'; -import { GroupByField } from './GroupByField'; import InlineSearchField from './InlineSearchField'; import SearchField from './SearchField'; import TagsInput from './TagsInput'; @@ -237,15 +237,15 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa addVariablesToOptions={addVariablesToOptions} /> - {config.featureToggles.metricsSummary && ( - - )} + { + delete query.groupBy; + onChange({ + ...query, + }); + }} + />
diff --git a/public/app/plugins/datasource/tempo/dataquery.cue b/public/app/plugins/datasource/tempo/dataquery.cue index ef5a4526dbb..c87f5eddb9e 100644 --- a/public/app/plugins/datasource/tempo/dataquery.cue +++ b/public/app/plugins/datasource/tempo/dataquery.cue @@ -47,7 +47,7 @@ composableKinds: DataQuery: { // Defines the maximum number of spans per spanset that are returned from Tempo spss?: int64 filters: [...#TraceqlFilter] - // Filters that are used to query the metrics summary + // deprecated Filters that are used to query the metrics summary groupBy?: [...#TraceqlFilter] // The type of the table that is used to display the search results tableType?: #SearchTableType diff --git a/public/app/plugins/datasource/tempo/dataquery.gen.ts b/public/app/plugins/datasource/tempo/dataquery.gen.ts index 757960330c8..f55757b5a47 100644 --- a/public/app/plugins/datasource/tempo/dataquery.gen.ts +++ b/public/app/plugins/datasource/tempo/dataquery.gen.ts @@ -17,7 +17,7 @@ export interface TempoQuery extends common.DataQuery { exemplars?: number; filters: Array; /** - * Filters that are used to query the metrics summary + * deprecated Filters that are used to query the metrics summary */ groupBy?: Array; /** diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index c51e27fdec8..cc5683941e8 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -172,16 +172,6 @@ describe('Tempo data source', () => { valueType: 'string', }, ], - groupBy: [ - { - id: 'groupBy1', - tag: '$interpolationVar', - }, - { - id: 'groupBy2', - tag: '$interpolationVar', - }, - ], }; } let templateSrv: TemplateSrv; @@ -214,8 +204,6 @@ describe('Tempo data source', () => { expect(queries[0].filters[0].value).toBe(textWithPipe); expect(queries[0].filters[1].value).toBe(text); expect(queries[0].filters[1].tag).toBe(text); - expect(queries[0].groupBy?.[0].tag).toBe(text); - expect(queries[0].groupBy?.[1].tag).toBe(text); }); it('when applying template variables', async () => { @@ -228,8 +216,6 @@ describe('Tempo data source', () => { expect(resp.filters[0].value).toBe(textWithPipe); expect(resp.filters[1].value).toBe(scopedText); expect(resp.filters[1].tag).toBe(scopedText); - expect(resp.groupBy?.[0].tag).toBe(scopedText); - expect(resp.groupBy?.[1].tag).toBe(scopedText); }); it('when serviceMapQuery is an array', async () => { @@ -352,18 +338,6 @@ describe('Tempo data source', () => { expect(edgesFrame.meta?.preferredVisualisationType).toBe('nodeGraph'); }); - it('should format metrics summary query correctly', () => { - const ds = new TempoDatasource(defaultSettings, {} as TemplateSrv); - const queryGroupBy = [ - { id: '1', scope: TraceqlSearchScope.Unscoped, tag: 'component' }, - { id: '2', scope: TraceqlSearchScope.Span, tag: 'name' }, - { id: '3', scope: TraceqlSearchScope.Resource, tag: 'service.name' }, - { id: '4', scope: TraceqlSearchScope.Intrinsic, tag: 'kind' }, - ]; - const groupBy = ds.formatGroupBy(queryGroupBy); - expect(groupBy).toEqual('.component, span.name, resource.service.name, kind'); - }); - describe('test the testDatasource function', () => { it('should return a success msg if response.ok is true', async () => { mockObservable = () => of({ ok: true }); diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index de07cc54e45..0791334f1c1 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -53,7 +53,6 @@ import { totalsMetric, } from './graphTransform'; import TempoLanguageProvider from './language_provider'; -import { createTableFrameFromMetricsSummaryQuery, emptyResponse, MetricsSummary } from './metricsSummary'; import { enhanceTraceQlMetricsResponse, formatTraceQLResponse, @@ -405,19 +404,18 @@ export class TempoDatasource extends DataSourceWithBackend this.hasGroupBy(t)); - if (target) { - const appliedQuery = this.applyVariables(target, options.scopedVars); - const queryFromFilters = this.languageProvider.generateQueryFromFilters(appliedQuery.filters); - subQueries.push(this.handleMetricsSummaryQuery(appliedQuery, queryFromFilters, options)); - } - } + if (targets.traceqlSearch[0].groupBy) { + return of({ + error: { + message: + 'The aggregate by query is deprecated. Please remove the current query and create a new one. Alternatively, you can use Traces Drilldown.', + }, + data: [], + }); + } - const traceqlSearchTargets = config.featureToggles.metricsSummary - ? targets.traceqlSearch.filter((t) => !this.hasGroupBy(t)) - : targets.traceqlSearch; + try { + const traceqlSearchTargets = targets.traceqlSearch; if (traceqlSearchTargets.length > 0) { const appliedQuery = this.applyVariables(traceqlSearchTargets[0], options.scopedVars); const queryFromFilters = this.languageProvider.generateQueryFromFilters(appliedQuery.filters); @@ -552,17 +550,6 @@ export class TempoDatasource extends DataSourceWithBackend { - const updatedFilter = { - ...filter, - tag: this.templateSrv.replace(filter.tag ?? '', scopedVars), - }; - - return updatedFilter; - }); - } - return { ...expandedQuery, query: this.templateSrv.replace(query.query ?? '', scopedVars, VariableFormatID.Pipe), @@ -572,22 +559,6 @@ export class TempoDatasource extends DataSourceWithBackend { - return groupBy - ?.filter((f) => f.tag) - .map((f) => { - if (f.scope === TraceqlSearchScope.Unscoped) { - return `.${f.tag}`; - } - return f.scope !== TraceqlSearchScope.Intrinsic ? `${f.scope}.${f.tag}` : f.tag; - }) - .join(', '); - }; - - hasGroupBy = (query: TempoQuery) => { - return query.groupBy?.find((gb) => gb.tag); - }; - /** * Handles the simplest of the queries where we have just a trace id and return trace data for it. * @param options @@ -731,93 +702,6 @@ export class TempoDatasource extends DataSourceWithBackend) => { - reportInteraction('grafana_traces_metrics_summary_queried', { - datasourceType: 'tempo', - app: options.app ?? '', - grafana_version: config.buildInfo.version, - filterCount: target.groupBy?.length ?? 0, - }); - - if (query === '{}') { - return of({ - error: { - message: - 'Please ensure you do not have an empty query. This is so filters are applied and the metrics summary is not generated from all spans.', - }, - data: emptyResponse, - }); - } - - const startTime = performance.now(); - const groupBy = target.groupBy ? this.formatGroupBy(target.groupBy) : ''; - return this._request('/api/metrics/summary', { - q: query, - groupBy, - start: options.range.from.unix(), - end: options.range.to.unix(), - }).pipe( - map((response) => { - if (!response.data.summaries) { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(`No summary data for '${groupBy}'.`), - }); - return { - error: { - message: getErrorMessage(`No summary data for '${groupBy}'.`), - }, - data: emptyResponse, - }; - } - // Check if any of the results have series data as older versions of Tempo placed the series data in a different structure - const hasSeries = response.data.summaries.some((summary: MetricsSummary) => summary.series.length > 0); - if (!hasSeries) { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), - }); - return { - error: { - message: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), - }, - data: emptyResponse, - }; - } - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: true, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - }); - return { - data: createTableFrameFromMetricsSummaryQuery(response.data.summaries, query, this.instanceSettings), - }; - }), - catchError((error) => { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(error.data.message), - statusCode: error.status, - statusText: error.statusText, - }); - return of({ - error: { message: getErrorMessage(error.data.message) }, - data: emptyResponse, - }); - }) - ); - }; - // This function can probably be simplified by avoiding passing both `targets` and `query`, // since `query` is built from `targets`, if you look at how this function is currently called handleStreamingQuery( diff --git a/public/app/plugins/datasource/tempo/language_provider.test.ts b/public/app/plugins/datasource/tempo/language_provider.test.ts index 7aff5eb7b18..9a91c94176a 100644 --- a/public/app/plugins/datasource/tempo/language_provider.test.ts +++ b/public/app/plugins/datasource/tempo/language_provider.test.ts @@ -8,38 +8,6 @@ import { intrinsics } from './traceql/traceql'; import { Scope } from './types'; describe('Language_provider', () => { - describe('should get correct metrics summary tags', () => { - it('for API v1 tags', async () => { - const lp = setup(v1Tags); - const tags = lp.getMetricsSummaryTags(); - expect(tags).toEqual(['bar', 'foo']); - }); - - it('for API v2 intrinsic tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Intrinsic); - expect(tags).toEqual(uniq(['duration', 'kind', 'name', 'status'].concat(intrinsics))); - }); - - it('for API v2 resource tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Resource); - expect(tags).toEqual(['cluster', 'container']); - }); - - it('for API v2 span tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Span); - expect(tags).toEqual(['db']); - }); - - it('for API v2 unscoped tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Unscoped); - expect(tags).toEqual(['cluster', 'container', 'db']); - }); - }); - describe('should get correct tags', () => { it('for API v1 tags', async () => { const lp = setup(v1Tags); diff --git a/public/app/plugins/datasource/tempo/language_provider.ts b/public/app/plugins/datasource/tempo/language_provider.ts index 63639645ef1..e8c4d202dde 100644 --- a/public/app/plugins/datasource/tempo/language_provider.ts +++ b/public/app/plugins/datasource/tempo/language_provider.ts @@ -97,18 +97,6 @@ export default class TempoLanguageProvider extends LanguageProvider { return []; }; - getMetricsSummaryTags = (scope?: TraceqlSearchScope) => { - if (this.tagsV2 && scope) { - if (scope === TraceqlSearchScope.Unscoped) { - return getUnscopedTags(this.tagsV2); - } - return getTagsByScope(this.tagsV2, scope); - } else if (this.tagsV1) { - return this.tagsV1; - } - return []; - }; - getTraceqlAutocompleteTags = (scope?: string) => { if (this.tagsV2) { if (!scope) { diff --git a/public/app/plugins/datasource/tempo/metricsSummary.test.ts b/public/app/plugins/datasource/tempo/metricsSummary.test.ts deleted file mode 100644 index 15b351e564e..00000000000 --- a/public/app/plugins/datasource/tempo/metricsSummary.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { defaultSettings } from './datasource.test'; -import { - createTableFrameFromMetricsSummaryQuery, - emptyResponse, - getConfigQuery, - transformToMetricsData, -} from './metricsSummary'; - -describe('MetricsSummary', () => { - describe('createTableFrameFromMetricsSummaryQuery', () => { - it('should return emptyResponse when state is LoadingState.Error', () => { - const result = createTableFrameFromMetricsSummaryQuery([], '', defaultSettings); - expect(result).toEqual(emptyResponse); - }); - - it('should return correctly when state is LoadingState.Done', () => { - const data = [ - { - spanCount: '10', - errorSpanCount: '1', - p50: '1', - p90: '2', - p95: '3', - p99: '4', - series: [ - { - key: 'span.http.status_code', - value: { - type: 3, - n: 208, - }, - }, - { - key: 'temperature', - value: { - type: 4, - f: 38.1, - }, - }, - ], - }, - ]; - const result = createTableFrameFromMetricsSummaryQuery( - data, - '{name="HTTP POST - post"} | by(resource.service.name)', - defaultSettings - ); - expect(result).toMatchInlineSnapshot(` - [ - { - "fields": [ - { - "config": { - "displayNameFromDS": "span.http.status_code", - "links": [ - { - "internal": { - "datasourceName": "tempo", - "datasourceUid": "gdev-tempo", - "query": { - "query": "{name="HTTP POST - post" && span.http.status_code=\${__data.fields["span.http.status_code"]} && temperature=\${__data.fields["temperature"]}} | by(resource.service.name)", - "queryType": "traceql", - }, - }, - "title": "Query in explore", - "url": "", - }, - ], - "noValue": "", - }, - "name": "span.http.status_code", - "type": "string", - "values": [ - 208, - ], - }, - { - "config": { - "displayNameFromDS": "temperature", - "links": [ - { - "internal": { - "datasourceName": "tempo", - "datasourceUid": "gdev-tempo", - "query": { - "query": "{name="HTTP POST - post" && span.http.status_code=\${__data.fields["span.http.status_code"]} && temperature=\${__data.fields["temperature"]}} | by(resource.service.name)", - "queryType": "traceql", - }, - }, - "title": "Query in explore", - "url": "", - }, - ], - "noValue": "", - }, - "name": "temperature", - "type": "string", - "values": [ - 38.1, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "Span count", - }, - "name": "spanCount", - "type": "number", - "values": [ - 10, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "Error", - "unit": "percent", - }, - "name": "errorPercentage", - "type": "number", - "values": [ - 10, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p50", - "unit": "ns", - }, - "name": "p50", - "type": "number", - "values": [ - 1, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p90", - "unit": "ns", - }, - "name": "p90", - "type": "number", - "values": [ - 2, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p95", - "unit": "ns", - }, - "name": "p95", - "type": "number", - "values": [ - 3, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p99", - "unit": "ns", - }, - "name": "p99", - "type": "number", - "values": [ - 4, - ], - }, - ], - "length": 1, - "meta": { - "preferredVisualisationType": "table", - }, - "name": "Metrics Summary", - "refId": "metrics-summary", - }, - ] - `); - }); - - it('transformToMetricsData should return correctly', () => { - const data = { - spanCount: '10', - errorSpanCount: '1', - p50: '1', - p90: '2', - p95: '3', - p99: '4', - series, - }; - const result = transformToMetricsData(data); - expect(result).toMatchInlineSnapshot(` - { - "contains_sink": "true", - "errorPercentage": 10, - "p50": 1, - "p90": 2, - "p95": 3, - "p99": 4, - "room": "kitchen", - "span.http.status_code": 208, - "spanCount": 10, - "spanKind": "server", - "spanStatus": "ok", - "temperature": 38.1, - "window_open": "8h", - } - `); - }); - - it('getConfigQuery should return correctly for empty target query', () => { - const result = getConfigQuery(series, '{}'); - expect(result).toEqual( - '{span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}}' - ); - }); - - it('getConfigQuery should return correctly for target query', () => { - const result = getConfigQuery(series, '{name="HTTP POST - post"} | by(resource.service.name)'); - expect(result).toEqual( - '{name="HTTP POST - post" && span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}} | by(resource.service.name)' - ); - }); - - it('getConfigQuery should return correctly for target query without brackets', () => { - const result = getConfigQuery(series, 'by(resource.service.name)'); - expect(result).toEqual( - '{span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}} | by(resource.service.name)' - ); - }); - }); -}); - -const series = [ - { - key: 'span.http.status_code', - value: { - type: 3, - n: 208, - }, - }, - { - key: 'temperature', - value: { - type: 4, - f: 38.1, - }, - }, - { - key: 'room', - value: { - type: 5, - s: 'kitchen', - }, - }, - { - key: 'contains_sink', - value: { - type: 6, - b: 'true', - }, - }, - { - key: 'window_open', - value: { - type: 7, - d: '8h', - }, - }, - { - key: 'spanStatus', - value: { - type: 8, - status: 1, - }, - }, - { - key: 'spanKind', - value: { - type: 9, - kind: 3, - }, - }, -]; diff --git a/public/app/plugins/datasource/tempo/metricsSummary.ts b/public/app/plugins/datasource/tempo/metricsSummary.ts deleted file mode 100644 index 9aebb32853c..00000000000 --- a/public/app/plugins/datasource/tempo/metricsSummary.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { - createDataFrame, - DataSourceInstanceSettings, - FieldDTO, - FieldType, - MutableDataFrame, - sortDataFrame, -} from '@grafana/data'; - -export type MetricsSummary = { - spanCount: string; - errorSpanCount?: string; - p50: string; - p90: string; - p95: string; - p99: string; - series: Series[]; -}; - -type Series = { - key: string; - value: { - type: number; - n?: number; - f?: number; - s?: string; - b?: string; - d?: string; - status?: number; - kind?: number; - }; -}; - -type MetricsData = { - spanCount: number; - errorPercentage: number | string; - p50: number; - p90: number; - p95: number; - p99: number; - [key: string]: string | number; -}; - -export function createTableFrameFromMetricsSummaryQuery( - data: MetricsSummary[], - targetQuery: string, - instanceSettings: DataSourceInstanceSettings -) { - let frame; - - if (!data.length) { - return emptyResponse; - } - - const dynamicMetrics: Record = {}; - data.forEach((res: MetricsSummary) => { - const configQuery = getConfigQuery(res.series, targetQuery); - res.series.forEach((series: Series) => { - dynamicMetrics[series.key] = { - name: `${series.key}`, - type: FieldType.string, - config: getConfig(series, configQuery, instanceSettings), - values: [], - }; - }); - }); - - frame = createDataFrame({ - name: 'Metrics Summary', - refId: 'metrics-summary', - fields: [ - ...Object.values(dynamicMetrics).sort((a, b) => a.name.localeCompare(b.name)), - { - name: 'spanCount', - type: FieldType.number, - config: { displayNameFromDS: 'Span count', custom: { width: 150 } }, - }, - { - name: 'errorPercentage', - type: FieldType.number, - config: { displayNameFromDS: 'Error', unit: 'percent', custom: { width: 150 } }, - }, - getPercentileRow('p50'), - getPercentileRow('p90'), - getPercentileRow('p95'), - getPercentileRow('p99'), - ], - meta: { - preferredVisualisationType: 'table', - }, - }); - - const metricsData = data.map(transformToMetricsData); - frame.length = metricsData.length; - for (const trace of metricsData) { - for (const field of frame.fields) { - field.values.push(trace[field.name]); - } - } - frame = sortDataFrame(frame, 0); - - return [frame]; -} - -export const transformToMetricsData = (data: MetricsSummary) => { - const errorPercentage = data.errorSpanCount - ? (getNumberForMetric(data.errorSpanCount) / getNumberForMetric(data.spanCount)) * 100 - : '0%'; - - const metricsData: MetricsData = { - spanCount: getNumberForMetric(data.spanCount), - errorPercentage, - p50: getNumberForMetric(data.p50), - p90: getNumberForMetric(data.p90), - p95: getNumberForMetric(data.p95), - p99: getNumberForMetric(data.p99), - }; - - data.series.forEach((series: Series) => { - metricsData[`${series.key}`] = getMetricValue(series) || ''; - }); - - return metricsData; -}; - -export const getConfigQuery = (series: Series[], targetQuery: string) => { - const queryParts = series.map((x: Series) => { - const isNumber = x.value.type === 3 || x.value.type === 4; - const isIntrinsic = x.value.type === 8 || x.value.type === 9; - const surround = isNumber || isIntrinsic ? '' : '"'; - return `${x.key}=${surround}` + '${__data.fields["' + x.key + '"]}' + `${surround}`; - }); - - let configQuery = ''; - const closingBracketIndex = targetQuery.indexOf('}'); - - if (closingBracketIndex !== -1) { - const queryAfterClosingBracket = targetQuery.substring(closingBracketIndex + 1); - configQuery = targetQuery.substring(0, closingBracketIndex); - if (queryParts.length > 0) { - configQuery += targetQuery.replace(/\s/g, '').includes('{}') ? '' : ' && '; - configQuery += `${queryParts.join(' && ')}`; - configQuery += `}`; - } - configQuery += `${queryAfterClosingBracket}`; - } else { - configQuery = `{${queryParts.join(' && ')}} | ${targetQuery}`; - } - - return configQuery; -}; - -const getConfig = (series: Series, query: string, instanceSettings: DataSourceInstanceSettings) => { - const commonConfig = { - displayNameFromDS: series.key, - noValue: '', - links: [ - { - title: 'Query in explore', - url: '', - internal: { - datasourceUid: instanceSettings.uid, - datasourceName: instanceSettings.name, - query: { - query, - queryType: 'traceql', - }, - }, - }, - ], - }; - - if (series.value.type === 7) { - return { - ...commonConfig, - unit: 'ns', - }; - } - return { ...commonConfig }; -}; - -const NO_VALUE = ''; - -const getMetricValue = (series: Series) => { - if (!series.value.type) { - return NO_VALUE; - } - - switch (series.value.type) { - case 3: - return series.value.n; - case 4: - return series.value.f; - case 5: - return series.value.s; - case 6: - return series.value.b; - case 7: - return series.value.d; - case 8: - return getSpanStatusCode(series.value.status); - case 9: - return getSpanKind(series.value.kind); - default: - return NO_VALUE; - } -}; - -// Values set according to Tempo enum: https://github.com/grafana/tempo/blob/main/pkg/traceql/enum_statics.go -const getSpanStatusCode = (statusCode: number | undefined) => { - if (!statusCode) { - return NO_VALUE; - } - - switch (statusCode) { - case 0: - return 'error'; - case 1: - return 'ok'; - default: - return 'unset'; - } -}; - -// Values set according to Tempo enum: https://github.com/grafana/tempo/blob/main/pkg/traceql/enum_statics.go -const getSpanKind = (kind: number | undefined) => { - if (!kind) { - return NO_VALUE; - } - - switch (kind) { - case 1: - return 'internal'; - case 2: - return 'client'; - case 3: - return 'server'; - case 4: - return 'producer'; - case 5: - return 'consumer'; - default: - return 'unspecified'; - } -}; - -const getPercentileRow = (name: string) => { - return { - name: name, - type: FieldType.number, - config: { - displayNameFromDS: name, - unit: 'ns', - custom: { - width: 150, - }, - }, - }; -}; - -const getNumberForMetric = (metric: string) => { - const number = parseInt(metric, 10); - return isNaN(number) ? 0 : number; -}; - -export const emptyResponse = new MutableDataFrame({ - name: 'Metrics Summary', - refId: 'metrics-summary', - fields: [], - meta: { - preferredVisualisationType: 'table', - }, -}); diff --git a/public/app/plugins/datasource/tempo/utils.ts b/public/app/plugins/datasource/tempo/utils.ts index 8bc5c2addd5..5c772239ff5 100644 --- a/public/app/plugins/datasource/tempo/utils.ts +++ b/public/app/plugins/datasource/tempo/utils.ts @@ -91,7 +91,6 @@ export const migrateFromSearchToTraceQLSearch = (query: TempoQuery) => { const migratedQuery: TempoQuery = { datasource: query.datasource, filters, - groupBy: query.groupBy, limit: query.limit, query: query.query, queryType: 'traceqlSearch', From 4f01c1aa885b9ca8cf0f098db692cfe0f7bcb182 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 24 Mar 2025 09:20:40 +0000 Subject: [PATCH 16/86] LDAP test: Fix page crash (#102587) * fix translation code on ldap page causing crash * move tooltip outside of trans --- .../app/features/admin/ldap/LdapUserGroups.tsx | 16 +++++++--------- public/locales/en-US/grafana.json | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/public/app/features/admin/ldap/LdapUserGroups.tsx b/public/app/features/admin/ldap/LdapUserGroups.tsx index 890ec4aee56..565048f1059 100644 --- a/public/app/features/admin/ldap/LdapUserGroups.tsx +++ b/public/app/features/admin/ldap/LdapUserGroups.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import { Tooltip, Icon, InteractiveTable, type CellProps, Column } from '@grafana/ui'; +import { Tooltip, Icon, InteractiveTable, type CellProps, Column, Stack } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; import { LdapRole } from 'app/types'; @@ -28,14 +28,12 @@ export const LdapUserGroups = ({ groups }: Props) => { header: 'Role', cell: (props: CellProps) => props.value || ( - <> - - No match{' '} - - - - - + + No match + + + + ), }, ], diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 48e03c5a9d6..1d1e1740791 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -136,7 +136,7 @@ "title": "LDAP Synchronization" }, "ldap-user-groups": { - "no-org-found": "No match <2><0>" + "no-org-found": "No match" }, "ldap-user-info": { "no-team": "No teams found via LDAP" From d25dba8d0a4c43cd96f8a3cb4a51b6f83451cbb2 Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:43:01 +0100 Subject: [PATCH 17/86] Alerting docs: additional details for `No Data` and `Error` alerts (#102593) * Alerting docs: additional details for `No Data` and `Error` alerts * add Error in the list of possible values --------- Co-authored-by: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> --- .../alert-rule-evaluation/state-and-health.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md index 5438c8c377b..79b137c1a7d 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md @@ -74,9 +74,14 @@ When an alert rule evaluation results in a `No Data` or `Error` state, Grafana A - `alertname`: Either `DatasourceNoData` or `DatasourceError` depending on the state. - `datasource_uid`: The UID of the data source that caused the state. +- `rulename`: The name of the alert rule that originated the alert. + +Note that `DatasourceNoData` and `DatasourceError` alert instances are independent from the original alert instance. They have different labels, which means existing silences, mute timings, and notification policies applied to the original alert may not apply to them. You can manage these alerts like regular ones by using their labels to apply actions such as adding a silence, routing via notification policies, and more. +If the alert rule is configured to send notifications directly to a selected contact point (instead of using notification policies), the `DatasourceNoData` and `DatasourceError` alerts are also sent to that contact point. Any additional notification settings defined in the alert rule, such as muting or grouping, are preserved. + ### Lifecycle of stale alert instances An alert instance is considered stale if its dimension or series has disappeared from the query results entirely for two evaluation intervals. @@ -85,9 +90,9 @@ Stale alert instances that are in the **Alerting**, **No Data**, or **Error** st ## Modify the `No Data` or `Error` state -In [Configure no data and error handling](ref:no-data-and-error-handling), you can change the default behaviour when the evaluation returns no data or an error. You can set the alert instance state to `Alerting`, `Normal`, or keep the last state. +These states are supported only for Grafana-managed alert rules. -Note that `No Data` and `Error` states are supported only for Grafana-managed alert rules. +In [Configure no data and error handling](ref:no-data-and-error-handling), you can change the default behaviour when the evaluation returns no data or an error. You can set the alert instance state to `Alerting`, `Normal`, `Error`, or `Keep Last State`. {{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} From 5d2dbd78833cbbb11972b6074cb1abb9031d8e25 Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Mon, 24 Mar 2025 11:00:50 +0100 Subject: [PATCH 18/86] Scopes: Move url sync from scenes to the ScopesSelectorService (#102469) * Add url sync * Move the URL sync handling to ScopesService to keep persistence * Reset location after tests * Prettier * Do url replace on enabling scopes * Remove comment * Update scenes version --- package.json | 4 +- public/app/features/scopes/ScopesApiClient.ts | 4 + .../features/scopes/ScopesContextProvider.tsx | 12 +- public/app/features/scopes/ScopesService.ts | 100 +++++++-- .../scopes/selector/ScopesSelectorService.ts | 205 +++++++++++------- .../scopes/tests/dashboardsList.test.ts | 3 +- .../features/scopes/tests/selector.test.ts | 3 +- public/app/features/scopes/tests/tree.test.ts | 3 +- yarn.lock | 37 ++-- 9 files changed, 256 insertions(+), 115 deletions(-) diff --git a/package.json b/package.json index 60d2e724c58..2d04f81fdc0 100644 --- a/package.json +++ b/package.json @@ -277,8 +277,8 @@ "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "6.5.2", - "@grafana/scenes-react": "6.5.2", + "@grafana/scenes": "6.5.3", + "@grafana/scenes-react": "6.5.3", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/scopes/ScopesApiClient.ts b/public/app/features/scopes/ScopesApiClient.ts index 443a7cad8c6..358d1b97e4d 100644 --- a/public/app/features/scopes/ScopesApiClient.ts +++ b/public/app/features/scopes/ScopesApiClient.ts @@ -62,6 +62,10 @@ export class ScopesApiClient { }); } + /** + * @param parent + * @param query Filters by title substring + */ async fetchNode(parent: string, query: string): Promise { try { const nodes = diff --git a/public/app/features/scopes/ScopesContextProvider.tsx b/public/app/features/scopes/ScopesContextProvider.tsx index 8e47b0e4b3a..9519b10be3b 100644 --- a/public/app/features/scopes/ScopesContextProvider.tsx +++ b/public/app/features/scopes/ScopesContextProvider.tsx @@ -1,6 +1,6 @@ -import { createContext, ReactNode, useMemo, useContext } from 'react'; +import { createContext, ReactNode, useMemo, useContext, useEffect } from 'react'; -import { config, ScopesContext } from '@grafana/runtime'; +import { config, locationService, ScopesContext } from '@grafana/runtime'; import { ScopesApiClient } from './ScopesApiClient'; import { ScopesService } from './ScopesService'; @@ -36,7 +36,7 @@ export function defaultScopesServices() { const dashboardService = new ScopesDashboardsService(client); const selectorService = new ScopesSelectorService(client, dashboardService); return { - scopesService: new ScopesService(selectorService, dashboardService), + scopesService: new ScopesService(selectorService, dashboardService, locationService), scopesSelectorService: selectorService, scopesDashboardsService: dashboardService, client, @@ -48,6 +48,12 @@ export const ScopesContextProvider = ({ children, services }: ScopesContextProvi return services ?? defaultScopesServices(); }, [services]); + useEffect(() => { + return () => { + memoizedServices.scopesService.cleanUp(); + }; + }, [memoizedServices]); + return ( diff --git a/public/app/features/scopes/ScopesService.ts b/public/app/features/scopes/ScopesService.ts index 2c836470194..21969652552 100644 --- a/public/app/features/scopes/ScopesService.ts +++ b/public/app/features/scopes/ScopesService.ts @@ -1,8 +1,8 @@ import { isEqual } from 'lodash'; -import { BehaviorSubject, Observable, combineLatest } from 'rxjs'; +import { BehaviorSubject, Observable, combineLatest, Subscription } from 'rxjs'; import { map, distinctUntilChanged } from 'rxjs/operators'; -import { ScopesContextValue, ScopesContextValueState } from '@grafana/runtime'; +import { LocationService, ScopesContextValue, ScopesContextValueState } from '@grafana/runtime'; import { ScopesDashboardsService } from './dashboards/ScopesDashboardsService'; import { ScopesSelectorService } from './selector/ScopesSelectorService'; @@ -24,9 +24,12 @@ export class ScopesService implements ScopesContextValue { // This will contain the combined state that will be public. private readonly _stateObservable: BehaviorSubject; + private subscriptions: Subscription[] = []; + constructor( private selectorService: ScopesSelectorService, - private dashboardsService: ScopesDashboardsService + private dashboardsService: ScopesDashboardsService, + private locationService: LocationService ) { this._state = new BehaviorSubject({ enabled: false, @@ -41,24 +44,64 @@ export class ScopesService implements ScopesContextValue { }); // We combine the latest emissions from this state + selectorService + dashboardsService. - combineLatest([ - this._state.asObservable(), - this.getSelectorServiceStateObservable(), - this.getDashboardsServiceStateObservable(), - ]) - .pipe( - // Map the 3 states into single ScopesContextValueState object - map( - ([thisState, selectorState, dashboardsState]): ScopesContextValueState => ({ - ...thisState, - value: selectorState.selectedScopes, - loading: selectorState.loading, - drawerOpened: dashboardsState.drawerOpened, - }) + this.subscriptions.push( + combineLatest([ + this._state.asObservable(), + this.getSelectorServiceStateObservable(), + this.getDashboardsServiceStateObservable(), + ]) + .pipe( + // Map the 3 states into single ScopesContextValueState object + map( + ([thisState, selectorState, dashboardsState]): ScopesContextValueState => ({ + ...thisState, + value: selectorState.selectedScopes, + loading: selectorState.loading, + drawerOpened: dashboardsState.drawerOpened, + }) + ) ) - ) - // We pass this into behaviourSubject so we get the 1 event buffer and we can access latest value. - .subscribe(this._stateObservable); + // We pass this into behaviourSubject so we get the 1 event buffer and we can access latest value. + .subscribe(this._stateObservable) + ); + + // Init from the URL when we first load + const queryParams = new URLSearchParams(locationService.getLocation().search); + this.changeScopes(queryParams.getAll('scopes')); + + // Update scopes state based on URL. + this.subscriptions.push( + locationService.getLocationObservable().subscribe((location) => { + if (!this.state.enabled) { + // We don't need to react on pages that don't interact with scopes. + return; + } + const queryParams = new URLSearchParams(location.search); + const scopes = queryParams.getAll('scopes'); + if (scopes.length) { + // We only update scopes but never delete them. This is to keep the scopes in memory if user navigates to + // page that does not use scopes (like from dashboard to dashboard list back to dashboard). If user + // changes the URL directly, it would trigger a reload so scopes would still be reset. + this.changeScopes(scopes); + } + }) + ); + + // Update the URL based on change in the scopes state + this.subscriptions.push( + selectorService.subscribeToState((state, prev) => { + const oldScopeNames = prev.selectedScopes.map((scope) => scope.scope.metadata.name); + const newScopeNames = state.selectedScopes.map((scope) => scope.scope.metadata.name); + if (!isEqual(oldScopeNames, newScopeNames)) { + this.locationService.partial( + { + scopes: newScopeNames, + }, + true + ); + } + }) + ); } /** @@ -97,6 +140,14 @@ export class ScopesService implements ScopesContextValue { public setEnabled = (enabled: boolean) => { if (this.state.enabled !== enabled) { this.updateState({ enabled }); + if (enabled) { + this.locationService.partial( + { + scopes: this.selectorService.state.selectedScopes.map(({ scope }) => scope.metadata.name), + }, + true + ); + } } }; @@ -127,4 +178,13 @@ export class ScopesService implements ScopesContextValue { distinctUntilChanged((prev, curr) => prev.drawerOpened === curr.drawerOpened) ); } + + /** + * Cleanup subscriptions so this can be garbage collected. + */ + public cleanUp() { + for (const sub of this.subscriptions) { + sub.unsubscribe(); + } + } } diff --git a/public/app/features/scopes/selector/ScopesSelectorService.ts b/public/app/features/scopes/selector/ScopesSelectorService.ts index 158be33d5f3..54b3aa6e5fb 100644 --- a/public/app/features/scopes/selector/ScopesSelectorService.ts +++ b/public/app/features/scopes/selector/ScopesSelectorService.ts @@ -1,4 +1,4 @@ -import { isEqual } from 'lodash'; +import { isEqual, last } from 'lodash'; import { ScopesApiClient } from '../ScopesApiClient'; import { ScopesServiceBase } from '../ScopesServiceBase'; @@ -14,7 +14,11 @@ export interface ScopesSelectorServiceState { opened: boolean; loadingNodeName: string | undefined; nodes: NodesMap; + + // Scopes that are selected and applied. selectedScopes: SelectedScope[]; + + // Representation of what is selected in the tree in the UI. This state may not be yet applied to the selectedScopes. treeScopes: TreeScope[]; } @@ -45,29 +49,47 @@ export class ScopesSelectorService extends ScopesServiceBase { - let nodes = { ...this.state.nodes }; + if (path.length < 1) { + return; + } + + // Making a copy as we will be changing this in place and then updating state later. + // This though does not make a deep copy so you cannot rely on reference of nested nodes changing. + const nodes = { ...this.state.nodes }; let currentLevel: NodesMap = nodes; + let loadingNodeName = path[0]; - for (let idx = 0; idx < path.length - 1; idx++) { - currentLevel = currentLevel[path[idx]].nodes; + if (path.length > 1) { + const pathToParent = path.slice(0, path.length - 1); + currentLevel = getNodesAtPath(nodes, pathToParent); + loadingNodeName = last(path)!; } - const loadingNodeName = path[path.length - 1]; const currentNode = currentLevel[loadingNodeName]; - const differentQuery = currentNode.query !== query; currentNode.expanded = expanded; currentNode.query = query; if (expanded || differentQuery) { + // Means we have to fetch the children of the node + this.updateState({ nodes, loadingNodeName }); - // fetchNodeApi does not throw just return empty object + // fetchNodeApi does not throw just returns empty object. + // Load all the children of the loadingNodeName const childNodes = await this.apiClient.fetchNode(loadingNodeName, query); if (loadingNodeName === this.state.loadingNodeName) { - const [selectedScopes, treeScopes] = this.getScopesAndTreeScopesWithPaths( + const [selectedScopes, treeScopes] = getScopesAndTreeScopesWithPaths( this.state.selectedScopes, this.state.treeScopes, path, @@ -95,6 +117,14 @@ export class ScopesSelectorService extends ScopesServiceBase { let treeScopes = [...this.state.treeScopes]; @@ -110,6 +140,8 @@ export class ScopesSelectorService extends ScopesServiceBase scopeName === linkId); if (selectedIdx === -1) { + // We prefetch the scope when clicking on it. This will mean that once the selection is applied in closeAndApply() + // we already have all the scopes in cache and don't need to fetch all of them again is multiple requests. this.apiClient.fetchScope(linkId!); const selectedFromSameNode = @@ -133,8 +165,14 @@ export class ScopesSelectorService extends ScopesServiceBase this.setNewScopes(scopeNames.map((scopeName) => ({ scopeName, path: [] }))); + /** + * Apply the selected scopes. Apart from setting the scopes it also fetches the scope metadata and also loads the + * related dashboards. + * @param treeScopes The scopes to be applied. If not provided the treeScopes state is used which was populated + * before for example by toggling the scopes in the scoped tree UI. + */ private setNewScopes = async (treeScopes = this.state.treeScopes) => { - if (isEqual(treeScopes, this.getTreeScopesFromSelectedScopes(this.state.selectedScopes))) { + if (isEqual(treeScopes, getTreeScopesFromSelectedScopes(this.state.selectedScopes))) { return; } @@ -142,7 +180,10 @@ export class ScopesSelectorService extends ScopesServiceBase scope.metadata.name)); selectedScopes = await this.apiClient.fetchMultipleScopes(treeScopes); @@ -151,6 +192,9 @@ export class ScopesSelectorService extends ScopesServiceBase this.setNewScopes([]); + /** + * Opens the scopes selector drawer and loads the root nodes if they are not loaded yet. + */ public open = async () => { if (Object.keys(this.state.nodes[''].nodes).length === 0) { await this.updateNode([''], true, ''); @@ -159,7 +203,7 @@ export class ScopesSelectorService extends ScopesServiceBase { - this.updateState({ opened: false, treeScopes: this.getTreeScopesFromSelectedScopes(this.state.selectedScopes) }); + // Reset the treeScopes if we don't want them actually applied. + this.updateState({ opened: false, treeScopes: getTreeScopesFromSelectedScopes(this.state.selectedScopes) }); }; public closeAndApply = () => { this.updateState({ opened: false }); this.setNewScopes(); }; +} - private closeNodes = (nodes: NodesMap): NodesMap => { - return Object.entries(nodes).reduce((acc, [id, node]) => { - acc[id] = { - ...node, - expanded: false, - nodes: this.closeNodes(node.nodes), - }; +/** + * Creates a deep copy of the node tree with expanded prop set to false. + * @param nodes + */ +function closeNodes(nodes: NodesMap): NodesMap { + return Object.entries(nodes).reduce((acc, [id, node]) => { + acc[id] = { + ...node, + expanded: false, + nodes: closeNodes(node.nodes), + }; - return acc; - }, {}); - }; + return acc; + }, {}); +} - private getTreeScopesFromSelectedScopes = (scopes: SelectedScope[]): TreeScope[] => { - return scopes.map(({ scope, path }) => ({ - scopeName: scope.metadata.name, - path, - })); - }; +function getTreeScopesFromSelectedScopes(scopes: SelectedScope[]): TreeScope[] { + return scopes.map(({ scope, path }) => ({ + scopeName: scope.metadata.name, + path, + })); +} - // helper func to get the selected/tree scopes together with their paths - // needed to maintain selected scopes in tree for example when navigating - // between categories or when loading scopes from URL to find the scope's path - private getScopesAndTreeScopesWithPaths = ( - selectedScopes: SelectedScope[], - treeScopes: TreeScope[], - path: string[], - childNodes: NodesMap - ): [SelectedScope[], TreeScope[]] => { - const childNodesArr = Object.values(childNodes); - - // Get all scopes without paths - // We use tree scopes as the list is always up to date as opposed to selected scopes which can be outdated - const scopeNamesWithoutPaths = treeScopes.filter(({ path }) => path.length === 0).map(({ scopeName }) => scopeName); - - // We search for the path of each scope name without a path - const scopeNamesWithPaths = scopeNamesWithoutPaths.reduce>((acc, scopeName) => { - const possibleParent = childNodesArr.find((childNode) => childNode.selectable && childNode.linkId === scopeName); - - if (possibleParent) { - acc[scopeName] = [...path, possibleParent.name]; - } +// helper func to get the selected/tree scopes together with their paths +// needed to maintain selected scopes in tree for example when navigating +// between categories or when loading scopes from URL to find the scope's path +function getScopesAndTreeScopesWithPaths( + selectedScopes: SelectedScope[], + treeScopes: TreeScope[], + path: string[], + childNodes: NodesMap +): [SelectedScope[], TreeScope[]] { + const childNodesArr = Object.values(childNodes); + + // Get all scopes without paths + // We use tree scopes as the list is always up to date as opposed to selected scopes which can be outdated + const scopeNamesWithoutPaths = treeScopes.filter(({ path }) => path.length === 0).map(({ scopeName }) => scopeName); + + // We search for the path of each scope name without a path + const scopeNamesWithPaths = scopeNamesWithoutPaths.reduce>((acc, scopeName) => { + const possibleParent = childNodesArr.find((childNode) => childNode.selectable && childNode.linkId === scopeName); + + if (possibleParent) { + acc[scopeName] = [...path, possibleParent.name]; + } - return acc; - }, {}); + return acc; + }, {}); - // Update the paths of the selected scopes based on what we found - const newSelectedScopes = selectedScopes.map((selectedScope) => { - if (selectedScope.path.length > 0) { - return selectedScope; - } + // Update the paths of the selected scopes based on what we found + const newSelectedScopes = selectedScopes.map((selectedScope) => { + if (selectedScope.path.length > 0) { + return selectedScope; + } - return { - ...selectedScope, - path: scopeNamesWithPaths[selectedScope.scope.metadata.name] ?? [], - }; - }); + return { + ...selectedScope, + path: scopeNamesWithPaths[selectedScope.scope.metadata.name] ?? [], + }; + }); - // Update the paths of the tree scopes based on what we found - const newTreeScopes = treeScopes.map((treeScope) => { - if (treeScope.path.length > 0) { - return treeScope; - } + // Update the paths of the tree scopes based on what we found + const newTreeScopes = treeScopes.map((treeScope) => { + if (treeScope.path.length > 0) { + return treeScope; + } - return { - ...treeScope, - path: scopeNamesWithPaths[treeScope.scopeName] ?? [], - }; - }); + return { + ...treeScope, + path: scopeNamesWithPaths[treeScope.scopeName] ?? [], + }; + }); - return [newSelectedScopes, newTreeScopes]; - }; + return [newSelectedScopes, newTreeScopes]; } function expandNodes(nodes: NodesMap, path: string[]): NodesMap { @@ -269,3 +318,13 @@ function expandNodes(nodes: NodesMap, path: string[]): NodesMap { return nodes; } + +function getNodesAtPath(nodes: NodesMap, path: string[]): NodesMap { + let currentNodes = nodes; + + for (const section of path) { + currentNodes = currentNodes[section].nodes; + } + + return currentNodes; +} diff --git a/public/app/features/scopes/tests/dashboardsList.test.ts b/public/app/features/scopes/tests/dashboardsList.test.ts index 8c89619b062..eec2829b144 100644 --- a/public/app/features/scopes/tests/dashboardsList.test.ts +++ b/public/app/features/scopes/tests/dashboardsList.test.ts @@ -1,4 +1,4 @@ -import { config } from '@grafana/runtime'; +import { config, locationService } from '@grafana/runtime'; import { ScopesService } from '../ScopesService'; import { ScopesDashboardsService } from '../dashboards/ScopesDashboardsService'; @@ -65,6 +65,7 @@ describe('Dashboards list', () => { }); afterEach(async () => { + locationService.replace(''); await resetScenes([fetchDashboardsSpy]); }); diff --git a/public/app/features/scopes/tests/selector.test.ts b/public/app/features/scopes/tests/selector.test.ts index 251d19c0db7..e430a95ab4f 100644 --- a/public/app/features/scopes/tests/selector.test.ts +++ b/public/app/features/scopes/tests/selector.test.ts @@ -1,4 +1,4 @@ -import { config } from '@grafana/runtime'; +import { config, locationService } from '@grafana/runtime'; import { getDashboardScenePageStateManager } from '../../dashboard-scene/pages/DashboardScenePageStateManager'; import { ScopesService } from '../ScopesService'; @@ -36,6 +36,7 @@ describe('Selector', () => { }); afterEach(async () => { + locationService.replace(''); await resetScenes([fetchSelectedScopesSpy, dashboardReloadSpy]); }); diff --git a/public/app/features/scopes/tests/tree.test.ts b/public/app/features/scopes/tests/tree.test.ts index 84f047a0768..51ebbc35219 100644 --- a/public/app/features/scopes/tests/tree.test.ts +++ b/public/app/features/scopes/tests/tree.test.ts @@ -1,4 +1,4 @@ -import { config } from '@grafana/runtime'; +import { config, locationService } from '@grafana/runtime'; import { ScopesService } from '../ScopesService'; import { ScopesSelectorService } from '../selector/ScopesSelectorService'; @@ -74,6 +74,7 @@ describe('Tree', () => { }); afterEach(async () => { + locationService.replace(''); await resetScenes([fetchNodesSpy, fetchScopeSpy]); }); diff --git a/yarn.lock b/yarn.lock index 284b4ebf097..fe969bf0465 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1416,7 +1416,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.10, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": +"@babel/runtime@npm:7.26.10": version: 7.26.10 resolution: "@babel/runtime@npm:7.26.10" dependencies: @@ -1425,6 +1425,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.26.9 + resolution: "@babel/runtime@npm:7.26.9" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10/08edd07d774eafbf157fdc8450ed6ddd22416fdd8e2a53e4a00349daba1b502c03ab7f7ad3ad3a7c46b9a24d99b5697591d0f852ee2f84642082ef7dda90b83d + languageName: node + linkType: hard + "@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9, @babel/template@npm:^7.3.3": version: 7.26.9 resolution: "@babel/template@npm:7.26.9" @@ -3471,11 +3480,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:6.5.2": - version: 6.5.2 - resolution: "@grafana/scenes-react@npm:6.5.2" +"@grafana/scenes-react@npm:6.5.3": + version: 6.5.3 + resolution: "@grafana/scenes-react@npm:6.5.3" dependencies: - "@grafana/scenes": "npm:6.5.2" + "@grafana/scenes": "npm:6.5.3" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3487,13 +3496,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/c922fea8ca86db49651a818640f32a7a35eeabcf886b52309ccd515a17bd071d19814e578158540299a4d01824ff3b9a05667bb86c91d87885c8ba7d0810c353 + checksum: 10/42630d527e3d9db2e2ae6728ad551b7cc9d36d2c3cdad3a6fdd5c8a959e2f88503767d257674db48480dc676bc83833a030a00dacd3bbd7058a51256f2c3b816 languageName: node linkType: hard -"@grafana/scenes@npm:6.5.2": - version: 6.5.2 - resolution: "@grafana/scenes@npm:6.5.2" +"@grafana/scenes@npm:6.5.3": + version: 6.5.3 + resolution: "@grafana/scenes@npm:6.5.3" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3511,7 +3520,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/267af84b2cc86acc547afbacb661aeffdd67388146bbf0f682ddd6577992973766daf7f8b35fb79ef5170ed10808f73294bfc8565b90fc4dd044dd738a34eba8 + checksum: 10/25a2041ba2e6a6bcac0e08d0493575ab26d3331c27db21d8398e9bdc32f6eb117f6620a4d07c2d20b0fbb02782e6b921932d134298f6765062f2872f6a9b972b languageName: node linkType: hard @@ -17683,8 +17692,8 @@ __metadata: "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/saga-icons": "workspace:*" - "@grafana/scenes": "npm:6.5.2" - "@grafana/scenes-react": "npm:6.5.2" + "@grafana/scenes": "npm:6.5.3" + "@grafana/scenes-react": "npm:6.5.3" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/tsconfig": "npm:^2.0.0" @@ -30524,7 +30533,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:11.0.5": +"uuid@npm:11.0.5, uuid@npm:^11.0.0, uuid@npm:^11.0.2": version: 11.0.5 resolution: "uuid@npm:11.0.5" bin: @@ -30542,7 +30551,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^11.0.0, uuid@npm:^11.0.2, uuid@npm:^11.0.5": +"uuid@npm:^11.0.5": version: 11.1.0 resolution: "uuid@npm:11.1.0" bin: From ddd3b825814dda16707cc1a8c10e9dfcc984efaf Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 24 Mar 2025 10:07:03 +0000 Subject: [PATCH 19/86] remove webpack aliasing for runtime + data (#102449) --- packages/grafana-data/package.json | 1 + packages/grafana-runtime/package.json | 2 +- scripts/webpack/webpack.dev.js | 5 ----- yarn.lock | 1 + 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 3bab0817d6e..82a8a8c46f9 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -39,6 +39,7 @@ "@grafana/schema": "12.0.0-pre", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.3", + "@types/systemjs": "6.15.1", "d3-interpolate": "3.0.1", "date-fns": "4.1.0", "dompurify": "3.2.4", diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 0f47def9b0a..3b0544d9ae5 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -42,6 +42,7 @@ "@grafana/faro-web-sdk": "^1.13.2", "@grafana/schema": "12.0.0-pre", "@grafana/ui": "12.0.0-pre", + "@types/systemjs": "6.15.1", "history": "4.10.1", "lodash": "4.17.21", "react-loading-skeleton": "3.5.0", @@ -62,7 +63,6 @@ "@types/lodash": "4.17.15", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", - "@types/systemjs": "6.15.1", "esbuild": "0.25.0", "lodash": "4.17.21", "react": "18.3.1", diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index 32caaa5fb9e..7d322fbf65b 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -52,11 +52,6 @@ module.exports = (env = {}) => { // Packages linked for development need react to be resolved from the same location react: path.resolve('./node_modules/react'), - // Also Grafana packages need to be resolved from the same location so they share - // the same singletons - '@grafana/runtime': path.resolve(__dirname, '../../packages/grafana-runtime'), - '@grafana/data': path.resolve(__dirname, '../../packages/grafana-data'), - // This is required to correctly resolve react-router-dom when linking with // local version of @grafana/scenes 'react-router-dom': path.resolve('./node_modules/react-router-dom'), diff --git a/yarn.lock b/yarn.lock index fe969bf0465..5e294290c31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2962,6 +2962,7 @@ __metadata: "@types/react": "npm:18.3.18" "@types/react-dom": "npm:18.3.5" "@types/string-hash": "npm:1.1.3" + "@types/systemjs": "npm:6.15.1" "@types/tinycolor2": "npm:1.4.6" d3-interpolate: "npm:3.0.1" date-fns: "npm:4.1.0" From c4549b59c550b6dd2e3ff5bdf3ec6cd97a61eff9 Mon Sep 17 00:00:00 2001 From: Denis Vodopianov Date: Mon, 24 Mar 2025 11:10:57 +0100 Subject: [PATCH 20/86] Chore: Remove drone from go managed tools (#102529) * remove drone from go managed tools * updating workspace after drone removal --- .citools/bra/go.mod | 2 +- .citools/bra/go.sum | 3 +- .citools/drone/go.mod | 77 ---- .citools/drone/go.sum | 338 ----------------- .citools/golangci-lint/go.mod | 64 ++-- .citools/golangci-lint/go.sum | 551 ++++------------------------ .citools/jb/go.mod | 2 +- .citools/jb/go.sum | 4 +- .citools/lefthook/go.mod | 2 +- .citools/lefthook/go.sum | 4 +- .citools/swagger/go.mod | 42 ++- .citools/swagger/go.sum | 137 +++---- Dockerfile | 1 - apps/alerting/notifications/go.mod | 2 + apps/alerting/notifications/go.sum | 8 +- apps/dashboard/go.mod | 4 +- apps/dashboard/go.sum | 7 +- go.mod | 4 +- go.sum | 7 +- go.work | 1 - go.work.sum | 20 +- pkg/aggregator/go.mod | 4 +- pkg/aggregator/go.sum | 7 +- pkg/promlib/go.mod | 4 +- pkg/promlib/go.sum | 7 +- pkg/storage/unified/apistore/go.mod | 4 +- pkg/storage/unified/apistore/go.sum | 7 +- pkg/storage/unified/resource/go.mod | 4 +- pkg/storage/unified/resource/go.sum | 7 +- 29 files changed, 244 insertions(+), 1080 deletions(-) delete mode 100644 .citools/drone/go.mod delete mode 100644 .citools/drone/go.sum diff --git a/.citools/bra/go.mod b/.citools/bra/go.mod index 7562899364f..66f123067be 100644 --- a/.citools/bra/go.mod +++ b/.citools/bra/go.mod @@ -5,7 +5,7 @@ go 1.24.1 tool github.com/unknwon/bra require ( - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect diff --git a/.citools/bra/go.sum b/.citools/bra/go.sum index c96783ededd..1b0afe3b97c 100644 --- a/.citools/bra/go.sum +++ b/.citools/bra/go.sum @@ -1,6 +1,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= diff --git a/.citools/drone/go.mod b/.citools/drone/go.mod deleted file mode 100644 index 4c1fba053f9..00000000000 --- a/.citools/drone/go.mod +++ /dev/null @@ -1,77 +0,0 @@ -module drone - -go 1.24.1 - -tool github.com/drone/drone-cli/drone - -replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible - -require ( - github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/bmatcuk/doublestar v1.3.4 // indirect - github.com/buildkite/yaml v2.1.0+incompatible // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-semver v0.3.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dchest/uniuri v1.2.0 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v27.5.1+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/drone-runners/drone-runner-docker v1.8.3 // indirect - github.com/drone/drone-cli v1.8.0 // indirect - github.com/drone/drone-go v1.7.1 // indirect - github.com/drone/envsubst v1.0.3 // indirect - github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f // indirect - github.com/drone/runner-go v1.12.0 // indirect - github.com/drone/signal v1.0.0 // indirect - github.com/fatih/color v1.18.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/go-jsonnet v0.18.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/term v0.5.0 // indirect - github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/urfave/cli v1.22.16 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect - google.golang.org/grpc v1.70.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gotest.tools/v3 v3.5.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) diff --git a/.citools/drone/go.sum b/.citools/drone/go.sum deleted file mode 100644 index 4e6b0dba301..00000000000 --- a/.citools/drone/go.sum +++ /dev/null @@ -1,338 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3cARGAK9CfW3HoxCy1a0G4TKrdiKke8ftOMEOHyySYs= -github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= -github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= -github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= -github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= -github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= -github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= -github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/drone-runners/drone-runner-docker v1.8.3 h1:uUnC45C1JMSLW+9uy6RoKG5ugzeXWN89pygs9BMLObY= -github.com/drone-runners/drone-runner-docker v1.8.3/go.mod h1:JR3pZeVZKKpkbTajiq0YtAx9WutkODdVKZGNR83kEwE= -github.com/drone/drone-cli v1.8.0 h1:tpp+GPonS87IKMZCGbIoa+zfDwiuJDL3NIC6S7neNrU= -github.com/drone/drone-cli v1.8.0/go.mod h1:zu6/7OpQjWBw/5VG0M3K4iJc6kSoTrjnY7CRLBrGH84= -github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw= -github.com/drone/drone-go v1.7.1/go.mod h1:fxCf9jAnXDZV1yDr0ckTuWd1intvcQwfJmTRpTZ1mXg= -github.com/drone/envsubst v1.0.2/go.mod h1:bkZbnc/2vh1M12Ecn7EYScpI4YGYU0etwLJICOWi8Z0= -github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= -github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= -github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f h1:/jEs7lulqVO2u1+XI5rW4oFwIIusxuDOVKD9PAzlW2E= -github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f/go.mod h1:nDRkX7PHq+p39AD5/usv3KZMerxZTYU/9rfLS5IDspU= -github.com/drone/runner-go v1.12.0 h1:zUjDj9ylsJ4n4Mvy4znddq/Z4EBzcUXzTltpzokKtgs= -github.com/drone/runner-go v1.12.0/go.mod h1:vu4pPPYDoeN6vdYQAY01GGGsAIW4aLganJNaa8Fx8zE= -github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI= -github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= -github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= -github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk= -github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/moby v27.5.1+incompatible h1:/pN59F/t3U7Q4FPzV88nzqf7Fp0qqCSL2KzhZaiKcKw= -github.com/moby/moby v27.5.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= -github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= -github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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/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= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -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= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= -google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -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.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/.citools/golangci-lint/go.mod b/.citools/golangci-lint/go.mod index 93df12ec8c3..46e327dbd7f 100644 --- a/.citools/golangci-lint/go.mod +++ b/.citools/golangci-lint/go.mod @@ -34,19 +34,19 @@ require ( github.com/butuzov/mirror v1.2.0 // indirect github.com/catenacyber/perfsprint v0.7.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.2 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect github.com/ckaznocha/intrange v0.2.1 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect github.com/daixiang0/gci v0.13.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.5 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.8 // indirect github.com/go-critic/go-critic v0.11.5 // indirect @@ -61,7 +61,6 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.12.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-printf-func-name v0.1.0 // indirect github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect @@ -71,7 +70,8 @@ require ( github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.5.3 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect @@ -96,31 +96,33 @@ require ( github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/macabu/inamedparam v0.1.3 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect + github.com/matryer/is v1.4.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mgechev/revive v1.5.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/moricho/tparallel v0.3.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.18.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.6.0 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect @@ -131,6 +133,8 @@ require ( github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/ryancurrah/gomodguard v1.3.5 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect @@ -141,18 +145,18 @@ require ( github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/tenv v1.12.1 // indirect github.com/sonatard/noctx v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.12.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.9.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect github.com/tetafro/godot v1.4.18 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect @@ -170,18 +174,18 @@ require ( gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.13.0 // indirect go-simpler.org/sloglint v0.7.2 // indirect - go.uber.org/atomic v1.7.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.24.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.27.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.36.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/golangci-lint/go.sum b/.citools/golangci-lint/go.sum index c8205a75d73..bc25b962103 100644 --- a/.citools/golangci-lint/go.sum +++ b/.citools/golangci-lint/go.sum @@ -2,39 +2,6 @@ 4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= 4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc= 4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= @@ -45,10 +12,8 @@ github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZ github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII= github.com/Antonboom/testifylint v1.5.0 h1:dlUIsDMtCrZWUnvkaCz3quJCoIjaGi41GzjPBGkkJ8A= github.com/Antonboom/testifylint v1.5.0/go.mod h1:wqaJbu0Blb5Wag2wv7Z5xt+CIV+eVLxtGZrlK13z3AE= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA= github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= @@ -65,11 +30,6 @@ github.com/alecthomas/go-check-sumtype v0.2.0 h1:Bo+e4DFf3rs7ME9w/0SU/g6nmzJaphd github.com/alecthomas/go-check-sumtype v0.2.0/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= @@ -80,10 +40,6 @@ github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8ger github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= @@ -104,35 +60,25 @@ github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyy github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/ckaznocha/intrange v0.2.1 h1:M07spnNEQoALOJhwrImSrJLaxwuiQK+hA2DeajBlwYk= github.com/ckaznocha/intrange v0.2.1/go.mod h1:7NEhVyf8fzZO5Ds7CRaqPEm52Ut83hsTiL5zbER/HYk= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -141,30 +87,20 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghostiam/protogetter v0.3.8 h1:LYcXbYvybUyTIxN2Mj9h6rHrDZBDwZloPoKctWrFyJY= github.com/ghostiam/protogetter v0.3.8/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= github.com/go-critic/go-critic v0.11.5 h1:TkDTOn5v7EEngMxu8KbuFqFR43USaaH8XRJLz1jhVYA= github.com/go-critic/go-critic v0.11.5/go.mod h1:wu6U7ny9PiaHaZHcvMDmdysMqvDem162Rh3zWTrqk8M= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= @@ -194,36 +130,6 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= @@ -242,37 +148,15 @@ github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAz github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= @@ -290,13 +174,10 @@ github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Rep github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= @@ -305,32 +186,18 @@ github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjz github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg= github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= @@ -349,16 +216,17 @@ github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84Yrj github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE= github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= +github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -367,23 +235,16 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgechev/revive v1.5.0 h1:oaSmjA7rP8+HyoRuCgC531VHwnLH1AlJdjj+1AnQceQ= github.com/mgechev/revive v1.5.0/go.mod h1:L6T3H8EoerRO86c7WuGpvohIUmiploGiyoYbtIWFmV8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= @@ -394,10 +255,10 @@ github.com/nunnatsa/ginkgolinter v0.18.0 h1:ZXO1wKhPg3A6LpbN5dMuqwhfOjN5c3ous8Yd github.com/nunnatsa/ginkgolinter v0.18.0/go.mod h1:vPrWafSULmjMGCMsfGA908if95VnHQNAahvSBOjTuWs= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -405,42 +266,23 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -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= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= @@ -456,7 +298,6 @@ github.com/raeperd/recvcheck v0.1.2/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtz github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -464,6 +305,10 @@ github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= @@ -478,9 +323,6 @@ github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqP github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= @@ -489,31 +331,30 @@ github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -521,10 +362,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= @@ -558,7 +399,6 @@ github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+ github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -572,63 +412,25 @@ go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k= golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -639,100 +441,36 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -740,12 +478,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -755,17 +490,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -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= 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= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -773,57 +506,16 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= @@ -837,121 +529,28 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/.citools/jb/go.mod b/.citools/jb/go.mod index a8df6197a17..ab4dca4ed3d 100644 --- a/.citools/jb/go.mod +++ b/.citools/jb/go.mod @@ -8,7 +8,7 @@ require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/jsonnet-bundler/jsonnet-bundler v0.5.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/.citools/jb/go.sum b/.citools/jb/go.sum index fa93a05ce6a..e8928e48705 100644 --- a/.citools/jb/go.sum +++ b/.citools/jb/go.sum @@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/jsonnet-bundler/jsonnet-bundler v0.5.1 h1:eUd6EA1Qzz73Q4NLNLOrNkMb96+6NTTERbX9lqaxVwk= github.com/jsonnet-bundler/jsonnet-bundler v0.5.1/go.mod h1:Qrdw/7mOFS2SKCOALKFfEH8gdvXJi8XZjw9g5ilpf4I= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= diff --git a/.citools/lefthook/go.mod b/.citools/lefthook/go.mod index 2fbfd58e665..2a4ace29be4 100644 --- a/.citools/lefthook/go.mod +++ b/.citools/lefthook/go.mod @@ -11,7 +11,7 @@ require ( github.com/charmbracelet/lipgloss v0.6.0 // indirect github.com/creack/pty v1.1.18 // indirect github.com/evilmartians/lefthook v1.4.8 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/google/go-cmp v0.7.0 // indirect diff --git a/.citools/lefthook/go.sum b/.citools/lefthook/go.sum index 1b0fbdb81ce..f93f56568c1 100644 --- a/.citools/lefthook/go.sum +++ b/.citools/lefthook/go.sum @@ -16,8 +16,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/evilmartians/lefthook v1.4.8 h1:8FmXWtfFiEZw3w18JbhVrp3g+Iy/j2XEo6gcC25+4KA= github.com/evilmartians/lefthook v1.4.8/go.mod h1:anwwu2QiCEnsOCBHfRgGOB3/sd9FMVNhmY8l9DDQAG8= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= diff --git a/.citools/swagger/go.mod b/.citools/swagger/go.mod index 80a7feab758..3e4a0cc8963 100644 --- a/.citools/swagger/go.mod +++ b/.citools/swagger/go.mod @@ -5,12 +5,13 @@ go 1.24.1 tool github.com/go-swagger/go-swagger/cmd/swagger require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/inflect v0.21.0 // indirect @@ -23,11 +24,11 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -35,30 +36,31 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.18.2 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/testify v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/toqueteos/webbrowser v1.2.0 // indirect - go.mongodb.org/mongo-driver v1.14.0 // indirect + go.mongodb.org/mongo-driver v1.16.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/swagger/go.sum b/.citools/swagger/go.sum index 53aa93f8a54..8c8679c2371 100644 --- a/.citools/swagger/go.sum +++ b/.citools/swagger/go.sum @@ -1,14 +1,14 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -16,8 +16,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= @@ -42,21 +42,16 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo= github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -69,113 +64,69 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= +go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Dockerfile b/Dockerfile index 76d1101e0e3..bb334ff8dfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,7 +65,6 @@ COPY .citools/cue .citools/cue COPY .citools/cog .citools/cog COPY .citools/lefthook .citools/lefthook COPY .citools/jb .citools/jb -COPY .citools/drone .citools/drone COPY .citools/golangci-lint .citools/golangci-lint COPY .citools/swagger ./citools/swagger diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index 6f0c3f390d0..17415e7048e 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -49,6 +49,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 8df3afad699..f613d6fe414 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -109,10 +109,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 8c2285debc3..a851c6e064b 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -12,7 +12,7 @@ require ( ) require ( - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -24,7 +24,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elazarl/goproxy v1.7.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/getkin/kin-openapi v0.129.0 // indirect diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 33cfb1d9e90..dfcbb8409bc 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -1,6 +1,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 h1:hXVi7QKuCQ0E8Yujfu9b0f0RnzZ72efpWvPnZgnJPrE= @@ -34,8 +35,8 @@ github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVo github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= diff --git a/go.mod b/go.mod index 15242ea90db..ae380cc710d 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.15.0 // @grafana/grafana-backend-group github.com/Azure/go-autorest/autorest v0.11.29 // @grafana/grafana-backend-group github.com/Azure/go-autorest/autorest/adal v0.9.24 // @grafana/grafana-backend-group - github.com/BurntSushi/toml v1.4.0 // @grafana/identity-access-team + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // @grafana/identity-access-team github.com/DATA-DOG/go-sqlmock v1.5.2 // @grafana/grafana-search-and-storage github.com/Masterminds/semver v1.5.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad @@ -44,7 +44,7 @@ require ( github.com/dlmiddlecote/sqlstats v1.0.2 // @grafana/grafana-backend-group github.com/dolthub/go-mysql-server v0.19.1-0.20250206012855-c216e59c21a7 // @grafana/grafana-datasources-core-services github.com/dolthub/vitess v0.0.0-20250123002143-3b45b8cacbfa // @grafana/grafana-datasources-core-services - github.com/fatih/color v1.17.0 // @grafana/grafana-backend-group + github.com/fatih/color v1.18.0 // @grafana/grafana-backend-group github.com/fullstorydev/grpchan v1.1.1 // @grafana/grafana-backend-group github.com/gchaincl/sqlhooks v1.3.0 // @grafana/grafana-search-and-storage github.com/getkin/kin-openapi v0.129.0 // @grafana/grafana-app-platform-squad diff --git a/go.sum b/go.sum index b8a9c60f8fb..82affcd7be1 100644 --- a/go.sum +++ b/go.sum @@ -706,8 +706,9 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4= @@ -1151,8 +1152,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= diff --git a/go.work b/go.work index 905df9f4e80..6d6bd805ff0 100644 --- a/go.work +++ b/go.work @@ -8,7 +8,6 @@ use ( ./.citools/bra ./.citools/cog ./.citools/cue - ./.citools/drone ./.citools/golangci-lint ./.citools/jb ./.citools/lefthook diff --git a/go.work.sum b/go.work.sum index fc6849458d4..dfa0340d910 100644 --- a/go.work.sum +++ b/go.work.sum @@ -508,6 +508,7 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06 git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8= git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng= +github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= @@ -800,6 +801,8 @@ github.com/dave/patsy v0.0.0-20210517141501-957256f50cba h1:1o36L4EKbZzazMk8iGC4 github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= +github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= +github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -812,6 +815,8 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -824,6 +829,9 @@ github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9X github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY= github.com/dolthub/swiss v0.2.1 h1:gs2osYs5SJkAaH5/ggVJqXQxRXtWshF6uE0lgR/Y3Gw= github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0= +github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f h1:/jEs7lulqVO2u1+XI5rW4oFwIIusxuDOVKD9PAzlW2E= +github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f/go.mod h1:nDRkX7PHq+p39AD5/usv3KZMerxZTYU/9rfLS5IDspU= +github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= @@ -859,7 +867,6 @@ github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c h1:yKN46XJHYC github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c/go.mod h1:L92h+dgwElEyUuShEwjbiHjseW410WIcNz+Bjutc8YQ= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -940,6 +947,8 @@ github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs github.com/google/generative-ai-go v0.18.0 h1:6ybg9vOCLcI/UpBBYXOTVgvKmcUKFRNj+2Cj3GnebSo= github.com/google/generative-ai-go v0.18.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= +github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8= github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= @@ -1046,6 +1055,8 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 h1:742eGXur0715JMq73 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk= +github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo= github.com/jaegertracing/jaeger v1.57.0 h1:3wDtUUPs6NRYH7+d+y8MilDkLHdpPrVlQ2wbcsA62bs= github.com/jaegertracing/jaeger v1.57.0/go.mod h1:p/1fxIU9hKHl7qEhKC72p2ZYVhvvZvNB73y6V7YyuTs= github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk= @@ -1057,6 +1068,8 @@ github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jon-whit/go-grpc-prometheus v1.4.0 h1:/wmpGDJcLXuEjXryWhVYEGt9YBRhtLwFEN7T+Flr8sw= github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao= github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU= @@ -1172,6 +1185,7 @@ github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5 h1:0KqC6/s github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= +github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= github.com/nats-io/nats.go v1.34.0 h1:fnxnPCNiwIG5w08rlMcEKTUw4AV/nKyGCOJE8TdhSPk= github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= @@ -1249,6 +1263,7 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30 h1:BHT1/DKsYDGkUgQ2jmMaozVcdk+sVfz0+1ZJq4zkWgw= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c h1:AwcgVYzW1T+QuJ2fc55ceOSCiVaOpdYUNpFj9t7+n9U= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -1315,9 +1330,9 @@ github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -1727,6 +1742,7 @@ google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index e6ca98240ed..eb227a8af37 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -22,7 +22,7 @@ require ( require ( cel.dev/expr v0.19.1 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect @@ -39,7 +39,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elazarl/goproxy v1.7.2 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index 0198e0efd67..e947b008d44 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -2,8 +2,9 @@ cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -60,8 +61,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 4b4393c9c70..020113b5982 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -18,7 +18,7 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect @@ -33,7 +33,7 @@ require ( github.com/dennwc/varint v1.0.0 // indirect github.com/elazarl/goproxy v1.7.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/getkin/kin-openapi v0.129.0 // indirect github.com/go-logr/logr v1.4.2 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 899f8550654..cbc5e25726a 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -13,8 +13,9 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkY github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -60,8 +61,8 @@ github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVo github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 14dc81193b1..c6b1c75bfda 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -51,7 +51,7 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect @@ -149,7 +149,7 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fullstorydev/grpchan v1.1.1 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index a74e2346a1f..d2ab1f07f14 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -649,8 +649,9 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= @@ -975,8 +976,8 @@ github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2T github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index bd4cea71221..e3aab925836 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -52,7 +52,7 @@ require ( github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect @@ -98,7 +98,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/envoyproxy/go-control-plane v0.13.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/getkin/kin-openapi v0.129.0 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 11f1f5bcb10..2ac29e1c27f 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -649,8 +649,9 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q= @@ -890,8 +891,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6Ni github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= From 35d22dc83d2b877aca4e9866c7fad4cf21156d50 Mon Sep 17 00:00:00 2001 From: Denis Vodopianov Date: Mon, 24 Mar 2025 11:11:13 +0100 Subject: [PATCH 21/86] Chore: Cleanup bwplotka/bingo managed tools (#102583) * cleanup biingo managed tools * add .bingo to prettier exclusions --- .bingo/README.md | 14 +- .bingo/Variables.mk | 55 +- .bingo/bra.mod | 7 - .bingo/bra.sum | 30 - .bingo/cog.mod | 5 - .bingo/cog.sum | 86 --- .bingo/cue.mod | 5 - .bingo/cue.sum | 58 -- .bingo/golangci-lint.mod | 5 - .bingo/golangci-lint.sum | 1174 -------------------------------------- .bingo/jb.mod | 5 - .bingo/jb.sum | 50 -- .bingo/lefthook.mod | 5 - .bingo/lefthook.sum | 523 ----------------- .bingo/swagger.mod | 5 - .bingo/swagger.sum | 810 -------------------------- .bingo/variables.env | 14 - .prettierignore | 1 + 18 files changed, 12 insertions(+), 2840 deletions(-) delete mode 100644 .bingo/bra.mod delete mode 100644 .bingo/bra.sum delete mode 100644 .bingo/cog.mod delete mode 100644 .bingo/cog.sum delete mode 100644 .bingo/cue.mod delete mode 100644 .bingo/cue.sum delete mode 100644 .bingo/golangci-lint.mod delete mode 100644 .bingo/golangci-lint.sum delete mode 100644 .bingo/jb.mod delete mode 100644 .bingo/jb.sum delete mode 100644 .bingo/lefthook.mod delete mode 100644 .bingo/lefthook.sum delete mode 100644 .bingo/swagger.mod delete mode 100644 .bingo/swagger.sum diff --git a/.bingo/README.md b/.bingo/README.md index 1d8a1360ccf..7a5c2d4f6da 100644 --- a/.bingo/README.md +++ b/.bingo/README.md @@ -2,13 +2,13 @@ This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. -- Run `bingo get` to install all tools having each own module file in this directory. -- Run `bingo get ` to install that have own module file in this directory. -- For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. -- For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. -- For go: Import `.bingo/variables.go` to for variable names. -- See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. +* Run `bingo get` to install all tools having each own module file in this directory. +* Run `bingo get ` to install that have own module file in this directory. +* For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. +* For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. +* For go: Import `.bingo/variables.go` to for variable names. +* See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. ## Requirements -- Go 1.14+ +* Go 1.14+ diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 80b5946b497..4160da8e0f7 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -5,68 +5,21 @@ GOPATH ?= $(shell go env GOPATH) GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin GO ?= $(shell which go) -# Add this near the top of the file, after the initial variable definitions -ifndef VARIABLES_MK -VARIABLES_MK := 1 - # Below generated variables ensure that every time a tool under each variable is invoked, the correct version # will be used; reinstalling only if needed. -# For example for bra variable: +# For example for drone variable: # # In your main Makefile (for non array binaries): # #include .bingo/Variables.mk # Assuming -dir was set to .bingo . # -#command: $(BRA) -# @echo "Running bra" -# @$(BRA) +#command: $(DRONE) +# @echo "Running drone" +# @$(DRONE) # -BRA := $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 -$(BRA): $(BINGO_DIR)/bra.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra" - -COG := $(GOBIN)/cog-v0.0.15 -$(COG): $(BINGO_DIR)/cog.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/cog-v0.0.15" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cog.mod -o=$(GOBIN)/cog-v0.0.15 "github.com/grafana/cog/cmd/cli" - -CUE := $(GOBIN)/cue-v0.5.0 -$(CUE): $(BINGO_DIR)/cue.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/cue-v0.5.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cue.mod -o=$(GOBIN)/cue-v0.5.0 "cuelang.org/go/cmd/cue" - DRONE := $(GOBIN)/drone-v1.5.0 $(DRONE): $(BINGO_DIR)/drone.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/drone-v1.5.0" @cd $(BINGO_DIR) && GOWORK=off CGO_ENABLED=0 $(GO) build -mod=mod -modfile=drone.mod -o=$(GOBIN)/drone-v1.5.0 "github.com/drone/drone-cli/drone" -GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.62.0 -$(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/golangci-lint-v1.62.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.62.0 "github.com/golangci/golangci-lint/cmd/golangci-lint" - -JB := $(GOBIN)/jb-v0.5.1 -$(JB): $(BINGO_DIR)/jb.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/jb-v0.5.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=jb.mod -o=$(GOBIN)/jb-v0.5.1 "github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb" - -LEFTHOOK := $(GOBIN)/lefthook-v1.4.8 -$(LEFTHOOK): $(BINGO_DIR)/lefthook.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/lefthook-v1.4.8" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=lefthook.mod -o=$(GOBIN)/lefthook-v1.4.8 "github.com/evilmartians/lefthook" - -SWAGGER := $(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37 -$(SWAGGER): $(BINGO_DIR)/swagger.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37 "github.com/go-swagger/go-swagger/cmd/swagger" - -endif diff --git a/.bingo/bra.mod b/.bingo/bra.mod deleted file mode 100644 index 0a64dc1765f..00000000000 --- a/.bingo/bra.mod +++ /dev/null @@ -1,7 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.17 - -replace golang.org/x/sys => golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c - -require github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 diff --git a/.bingo/bra.sum b/.bingo/bra.sum deleted file mode 100644 index d4e9da39ec8..00000000000 --- a/.bingo/bra.sum +++ /dev/null @@ -1,30 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI= -github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4= -github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= -github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= -github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 h1:4EYQaWAatQokdji3zqZloVIW/Ke1RQjYw2zHULyrHJg= -github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/sys v0.0.0-20191020152052-9984515f0562 h1:wOweSabW7qssfcg63CEDHHA4zyoqRlGU6eYV7IUMCq0= -golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= \ No newline at end of file diff --git a/.bingo/cog.mod b/.bingo/cog.mod deleted file mode 100644 index c6c7b46ef7d..00000000000 --- a/.bingo/cog.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.23.4 - -require github.com/grafana/cog v0.0.15 // cmd/cli diff --git a/.bingo/cog.sum b/.bingo/cog.sum deleted file mode 100644 index 8280a813d47..00000000000 --- a/.bingo/cog.sum +++ /dev/null @@ -1,86 +0,0 @@ -cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= -cuelang.org/go v0.11.0 h1:2af2nhipqlUHtXk2dtOP5xnMm1ObGvKqIsJUJL1sRE4= -cuelang.org/go v0.11.0/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0= -github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= -github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY= -github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= -github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= -github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= -github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= -github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= -github.com/grafana/cog v0.0.14 h1:sBK89oSu9BK4S9l3G9ewVJnGYnNQJTHFBC/01DZDRZs= -github.com/grafana/cog v0.0.14/go.mod h1:HwJbc60fZ+viayROClLGdDwO5w/JjBOpO9wjGnAfMLc= -github.com/grafana/cog v0.0.15 h1:e2pMY+Hf2nS22HcKJuguEzl0BVmV9DSINwCfWt+dFZQ= -github.com/grafana/cog v0.0.15/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= -github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= -github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= -github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= -github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.bingo/cue.mod b/.bingo/cue.mod deleted file mode 100644 index 6e13b6d3eec..00000000000 --- a/.bingo/cue.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.19 - -require cuelang.org/go v0.5.0 // cmd/cue diff --git a/.bingo/cue.sum b/.bingo/cue.sum deleted file mode 100644 index 5e9012b2946..00000000000 --- a/.bingo/cue.sum +++ /dev/null @@ -1,58 +0,0 @@ -cuelang.org/go v0.4.3 h1:W3oBBjDTm7+IZfCKZAmC8uDG0eYfJL4Pp/xbbCMKaVo= -cuelang.org/go v0.4.3/go.mod h1:7805vR9H+VoBNdWFdI7jyDR3QLUPp4+naHfbcgp55HI= -cuelang.org/go v0.5.0-beta.2 h1:am5M7jGvNTJ0rnjrFNyvE7fucL/wRqb0emK4XxdThQI= -cuelang.org/go v0.5.0-beta.2/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= -cuelang.org/go v0.5.0 h1:D6N0UgTGJCOxFKU8RU+qYvavKNsVc/+ZobmifStVJzU= -cuelang.org/go v0.5.0/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= -github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= -github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw= -github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= -github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= -github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0= -github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4= -github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ= -golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9 h1:VtCrPQXM5Wo9l7XN64SjBMczl48j8mkP+2e3OhYlz+0= -golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/tools v0.0.0-20200612220849-54c614fe050c h1:g6oFfz6Cmw68izP3xsdud3Oxu145IPkeFzyRg58AKHM= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod deleted file mode 100644 index 73e12b0e2ed..00000000000 --- a/.bingo/golangci-lint.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.23 - -require github.com/golangci/golangci-lint v1.62.0 // cmd/golangci-lint diff --git a/.bingo/golangci-lint.sum b/.bingo/golangci-lint.sum deleted file mode 100644 index 6e18a455fa9..00000000000 --- a/.bingo/golangci-lint.sum +++ /dev/null @@ -1,1174 +0,0 @@ -4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA= -4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= -4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc= -4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/4meepo/tagalign v1.3.3 h1:ZsOxcwGD/jP4U/aw7qeWu58i7dwYemfy5Y+IF1ACoNw= -github.com/4meepo/tagalign v1.3.3/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= -github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= -github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= -github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= -github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= -github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY= -github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM= -github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= -github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= -github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClDcQY= -github.com/Antonboom/errname v0.1.12/go.mod h1:bK7todrzvlaZoQagP1orKzWXv59X/x0W0Io2XT1Ssro= -github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= -github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= -github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA= -github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI= -github.com/Antonboom/nilnil v0.1.7 h1:ofgL+BA7vlA1K2wNQOsHzLJ2Pw5B5DpWRLdDAVvvTow= -github.com/Antonboom/nilnil v0.1.7/go.mod h1:TP+ScQWVEq0eSIxqU8CbdT5DFWoHp0MbP+KMUO1BKYQ= -github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= -github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= -github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZk= -github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII= -github.com/Antonboom/testifylint v1.2.0 h1:015bxD8zc5iY8QwTp4+RG9I4kIbqwvGX9TrBbb7jGdM= -github.com/Antonboom/testifylint v1.2.0/go.mod h1:rkmEqjqVnHDRNsinyN6fPSLnoajzFwsCcguJgwADBkw= -github.com/Antonboom/testifylint v1.3.0 h1:UiqrddKs1W3YK8R0TUuWwrVKlVAnS07DTUVWWs9c+y4= -github.com/Antonboom/testifylint v1.3.0/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM= -github.com/Antonboom/testifylint v1.3.1 h1:Uam4q1Q+2b6H7gvk9RQFw6jyVDdpzIirFOOrbs14eG4= -github.com/Antonboom/testifylint v1.3.1/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM= -github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= -github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= -github.com/Antonboom/testifylint v1.5.0 h1:dlUIsDMtCrZWUnvkaCz3quJCoIjaGi41GzjPBGkkJ8A= -github.com/Antonboom/testifylint v1.5.0/go.mod h1:wqaJbu0Blb5Wag2wv7Z5xt+CIV+eVLxtGZrlK13z3AE= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk= -github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0= -github.com/Crocmagnon/fatcontext v0.4.0 h1:4ykozu23YHA0JB6+thiuEv7iT6xq995qS1vcuWZq0tg= -github.com/Crocmagnon/fatcontext v0.4.0/go.mod h1:ZtWrXkgyfsYPzS6K3O88va6t2GEglG93vnII/F94WC0= -github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA= -github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= -github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= -github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= -github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= -github.com/alecthomas/go-check-sumtype v0.2.0 h1:Bo+e4DFf3rs7ME9w/0SU/g6nmzJaphduP8Cjiz0gbwY= -github.com/alecthomas/go-check-sumtype v0.2.0/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= -github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= -github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= -github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= -github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= -github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= -github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= -github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= -github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= -github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= -github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= -github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= -github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= -github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= -github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= -github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM= -github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= -github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= -github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= -github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= -github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= -github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs= -github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos= -github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= -github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= -github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk= -github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= -github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= -github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= -github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= -github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= -github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs= -github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ= -github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc= -github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= -github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= -github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= -github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= -github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= -github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/ckaznocha/intrange v0.1.0 h1:ZiGBhvrdsKpoEfzh9CjBfDSZof6QB0ORY5tXasUtiew= -github.com/ckaznocha/intrange v0.1.0/go.mod h1:Vwa9Ekex2BrEQMg6zlrWwbs/FtYw7eS5838Q7UjK7TQ= -github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= -github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= -github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs= -github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE= -github.com/ckaznocha/intrange v0.2.1 h1:M07spnNEQoALOJhwrImSrJLaxwuiQK+hA2DeajBlwYk= -github.com/ckaznocha/intrange v0.2.1/go.mod h1:7NEhVyf8fzZO5Ds7CRaqPEm52Ut83hsTiL5zbER/HYk= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= -github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= -github.com/daixiang0/gci v0.12.3 h1:yOZI7VAxAGPQmkb1eqt5g/11SUlwoat1fSblGLmdiQc= -github.com/daixiang0/gci v0.12.3/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= -github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= -github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= -github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= -github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= -github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= -github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= -github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= -github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= -github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= -github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/ghostiam/protogetter v0.3.5 h1:+f7UiF8XNd4w3a//4DnusQ2SZjPkUjxkMEfjbxOK4Ug= -github.com/ghostiam/protogetter v0.3.5/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= -github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= -github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= -github.com/ghostiam/protogetter v0.3.8 h1:LYcXbYvybUyTIxN2Mj9h6rHrDZBDwZloPoKctWrFyJY= -github.com/ghostiam/protogetter v0.3.8/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= -github.com/go-critic/go-critic v0.11.2 h1:81xH/2muBphEgPtcwH1p6QD+KzXl2tMSi3hXjBSxDnM= -github.com/go-critic/go-critic v0.11.2/go.mod h1:OePaicfjsf+KPy33yq4gzv6CO7TEQ9Rom6ns1KsJnl8= -github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU= -github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc= -github.com/go-critic/go-critic v0.11.5 h1:TkDTOn5v7EEngMxu8KbuFqFR43USaaH8XRJLz1jhVYA= -github.com/go-critic/go-critic v0.11.5/go.mod h1:wu6U7ny9PiaHaZHcvMDmdysMqvDem162Rh3zWTrqk8M= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= -github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= -github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= -github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= -github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= -github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= -github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= -github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= -github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= -github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= -github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= -github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= -github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= -github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= -github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= -github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= -github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= -github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= -github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= -github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= -github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= -github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= -github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= -github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= -github.com/golangci/golangci-lint v1.57.1 h1:cqhpzkzjDwdN12rfMf1SUyyKyp88a1SltNqEYGS0nJw= -github.com/golangci/golangci-lint v1.57.1/go.mod h1:zLcHhz3NHc88T5zV2j75lyc0zH3LdOPOybblYa4p0oI= -github.com/golangci/golangci-lint v1.59.0 h1:st69YDnAH/v2QXDcgUaZ0seQajHScPALBVkyitYLXEk= -github.com/golangci/golangci-lint v1.59.0/go.mod h1:QNA32UWdUdHXnu+Ap5/ZU4WVwyp2tL94UxEXrSErjg0= -github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOId1h3TTks= -github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg= -github.com/golangci/golangci-lint v1.60.1 h1:DRKNqNTQRLBJZ1il5u4fvgLQCjQc7QFs0DbhksJtVJE= -github.com/golangci/golangci-lint v1.60.1/go.mod h1:jDIPN1rYaIA+ijp9OZcUmUCoQOtZ76pOlFbi15FlLJY= -github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8= -github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8= -github.com/golangci/golangci-lint v1.62.0 h1:/G0g+bi1BhmGJqLdNQkKBWjcim8HjOPc4tsKuHDOhcI= -github.com/golangci/golangci-lint v1.62.0/go.mod h1:jtoOhQcKTz8B6dGNFyfQV3WZkQk+YvBDewDtNpiAJts= -github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g= -github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= -github.com/golangci/misspell v0.5.1 h1:/SjR1clj5uDjNLwYzCahHwIOPmQgoH04AyQIiWGbhCM= -github.com/golangci/misspell v0.5.1/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= -github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= -github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= -github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= -github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= -github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= -github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= -github.com/golangci/revgrep v0.5.2 h1:EndcWoRhcnfj2NHQ+28hyuXpLMF+dQmCN+YaeeIl4FU= -github.com/golangci/revgrep v0.5.2/go.mod h1:bjAMA+Sh/QUfTDcHzxfyHxr4xKvllVr/0sCv2e7jJHA= -github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs= -github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= -github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= -github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= -github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= -github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= -github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70= -github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= -github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= -github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= -github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.7.0 h1:cEqH+YBKLsECnRSd4F4TK5ri8t/aXtt/qoL0Ft252B0= -github.com/jgautheron/goconst v1.7.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= -github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= -github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= -github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= -github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= -github.com/jjti/go-spancheck v0.5.3 h1:vfq4s2IB8T3HvbpiwDTYgVPj1Ze/ZSXrTtaZRTc7CuM= -github.com/jjti/go-spancheck v0.5.3/go.mod h1:eQdOX1k3T+nAKvZDyLC3Eby0La4dZ+I19iOl5NzSPFE= -github.com/jjti/go-spancheck v0.6.1 h1:ZK/wE5Kyi1VX3PJpUO2oEgeoI4FWOUm7Shb2Gbv5obI= -github.com/jjti/go-spancheck v0.6.1/go.mod h1:vF1QkOO159prdo6mHRxak2CpzDpHAfKiPUDP/NeRnX8= -github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= -github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= -github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -github.com/karamaru-alpha/copyloopvar v1.0.8 h1:gieLARwuByhEMxRwM3GRS/juJqFbLraftXIKDDNJ50Q= -github.com/karamaru-alpha/copyloopvar v1.0.8/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= -github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= -github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= -github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= -github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= -github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg= -github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt0GMb8= -github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg= -github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= -github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= -github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= -github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= -github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= -github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= -github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I= -github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= -github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= -github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= -github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= -github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= -github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= -github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= -github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU= -github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= -github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= -github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= -github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= -github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= -github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= -github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= -github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= -github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= -github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= -github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE= -github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE= -github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA= -github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= -github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= -github.com/mgechev/revive v1.5.0 h1:oaSmjA7rP8+HyoRuCgC531VHwnLH1AlJdjj+1AnQceQ= -github.com/mgechev/revive v1.5.0/go.mod h1:L6T3H8EoerRO86c7WuGpvohIUmiploGiyoYbtIWFmV8= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA= -github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI= -github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= -github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= -github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= -github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= -github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= -github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.16.1 h1:uDIPSxgVHZ7PgbJElRDGzymkXH+JaF7mjew+Thjnt6Q= -github.com/nunnatsa/ginkgolinter v0.16.1/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= -github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk= -github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= -github.com/nunnatsa/ginkgolinter v0.18.0 h1:ZXO1wKhPg3A6LpbN5dMuqwhfOjN5c3ous8YdKOuqk9k= -github.com/nunnatsa/ginkgolinter v0.18.0/go.mod h1:vPrWafSULmjMGCMsfGA908if95VnHQNAahvSBOjTuWs= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= -github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.8 h1:jiEjKDH33ouFktyez7sckv6pHWif9B7SuS8cutDXFHw= -github.com/polyfloyd/go-errorlint v1.4.8/go.mod h1:NNCxFcFjZcw3xNjVdCchERkEM6Oz7wta2XJVxRftwO4= -github.com/polyfloyd/go-errorlint v1.5.1 h1:5gHxDjLyyWij7fhfrjYNNlHsUNQeyx0LFQKUelO3RBo= -github.com/polyfloyd/go-errorlint v1.5.1/go.mod h1:sH1QC1pxxi0fFecsVIzBmxtrgd9IF/SkJpA6wqyKAJs= -github.com/polyfloyd/go-errorlint v1.5.2 h1:SJhVik3Umsjh7mte1vE0fVZ5T1gznasQG3PV7U5xFdA= -github.com/polyfloyd/go-errorlint v1.5.2/go.mod h1:sH1QC1pxxi0fFecsVIzBmxtrgd9IF/SkJpA6wqyKAJs= -github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= -github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -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= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= -github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= -github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= -github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= -github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= -github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= -github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= -github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= -github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= -github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= -github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/raeperd/recvcheck v0.1.2 h1:SjdquRsRXJc26eSonWIo8b7IMtKD3OAT2Lb5G3ZX1+4= -github.com/raeperd/recvcheck v0.1.2/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.3.1 h1:fH+fUg+ngsQO0ruZXXHnA/2aNllWA1whly4a6UvyzGE= -github.com/ryancurrah/gomodguard v1.3.1/go.mod h1:DGFHzEhi6iJ0oIDfMuo3TgrS+L9gZvrEfmjjuelnRU0= -github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3IXxM18= -github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o= -github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBrAZfqpg= -github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= -github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= -github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= -github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= -github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= -github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= -github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.25.0 h1:IK8SI2QyFzy/2OD2PYnhy84dpfNo9qADrRt6LH8vSzU= -github.com/sashamelentyev/usestdlibvars v1.25.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/sashamelentyev/usestdlibvars v1.26.0 h1:LONR2hNVKxRmzIrZR0PhSF3mhCAzvnr+DcUiHgREfXE= -github.com/sashamelentyev/usestdlibvars v1.26.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= -github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/securego/gosec/v2 v2.19.0 h1:gl5xMkOI0/E6Hxx0XCY2XujA3V7SNSefA8sC+3f1gnk= -github.com/securego/gosec/v2 v2.19.0/go.mod h1:hOkDcHz9J/XIgIlPDXalxjeVYsHxoWUc5zJSHxcB8YM= -github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQwv8AyxzuwsVohljJgp4L0ZqiCgtACsPsc= -github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs= -github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M= -github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU= -github.com/securego/gosec/v2 v2.21.4 h1:Le8MSj0PDmOnHJgUATjD96PaXRvCpKC+DGJvwyy0Mlk= -github.com/securego/gosec/v2 v2.21.4/go.mod h1:Jtb/MwRQfRxCXyCm1rfM1BEiiiTfUOdyzzAhlr6lUTA= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= -github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= -github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= -github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0= -github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY= -github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= -github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= -github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= -github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= -github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= -github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= -github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= -github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= -github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= -github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= -github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk= -github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= -github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= -github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= -github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= -github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs= -github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= -github.com/tetafro/godot v1.4.18 h1:ouX3XGiziKDypbpXqShBfnNLTSjR8r3/HVzrtJ+bHlI= -github.com/tetafro/godot v1.4.18/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= -github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= -github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= -github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= -github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= -github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= -github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= -github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs= -github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= -github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4= -github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= -github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= -github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= -github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= -github.com/ultraware/whitespace v0.1.0 h1:O1HKYoh0kIeqE8sFqZf1o0qbORXUCOQFrlaQyZsczZw= -github.com/ultraware/whitespace v0.1.0/go.mod h1:/se4r3beMFNmewJ4Xmz0nMQ941GJt+qmSHGP9emHYe0= -github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ= -github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= -github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= -github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= -github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= -github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= -github.com/uudashr/iface v1.2.0 h1:ECJjh5q/1Zmnv/2yFpWV6H3oMg5+Mo+vL0aqw9Gjazo= -github.com/uudashr/iface v1.2.0/go.mod h1:Ux/7d/rAF3owK4m53cTVXL4YoVHKNqnoOeQHn2xrlp0= -github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= -github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= -github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= -github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= -github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= -github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= -github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= -github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= -github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= -github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/bosi/decorder v0.4.1 h1:VdsdfxhstabyhZovHafFw+9eJ6eU0d2CkFNJcZz/NU4= -gitlab.com/bosi/decorder v0.4.1/go.mod h1:jecSqWUew6Yle1pCr2eLWTensJMmsxHsBwt+PVbkAqA= -gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= -gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= -go-simpler.org/musttag v0.9.0 h1:Dzt6/tyP9ONr5g9h9P3cnYWCxeBFRkd0uJL/w+1Mxos= -go-simpler.org/musttag v0.9.0/go.mod h1:gA9nThnalvNSKpEoyp3Ko4/vCX2xTpqKoUtNqXOnVR4= -go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= -go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= -go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= -go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= -go-simpler.org/sloglint v0.5.0 h1:2YCcd+YMuYpuqthCgubcF5lBSjb6berc5VMOYUHKrpY= -go-simpler.org/sloglint v0.5.0/go.mod h1:EUknX5s8iXqf18KQxKnaBHUPVriiPnOrPjjJcsaTcSQ= -go-simpler.org/sloglint v0.7.0 h1:rMZRxD9MbaGoRFobIOicMxZzum7AXNFDlez6xxJs5V4= -go-simpler.org/sloglint v0.7.0/go.mod h1:g9SXiSWY0JJh4LS39/Q0GxzP/QX2cVcbTOYhDpXrJEs= -go-simpler.org/sloglint v0.7.1 h1:qlGLiqHbN5islOxjeLXoPtUdZXb669RW+BDQ+xOSNoU= -go-simpler.org/sloglint v0.7.1/go.mod h1:OlaVDRh/FKKd4X4sIMbsz8st97vomydceL146Fthh/c= -go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= -go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= -golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -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.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.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/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -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= -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= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -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.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs= -honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= -honnef.co/go/tools v0.5.0 h1:29uoiIormS3Z6R+t56STz/oI4v+mB51TSmEOdJPgRnE= -honnef.co/go/tools v0.5.0/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= -honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= -honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= -mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= -mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= -mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= -mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= -mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 h1:zCr3iRRgdk5eIikZNDphGcM6KGVTx3Yu+/Uu9Es254w= -mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI= -mvdan.cc/unparam v0.0.0-20240427195214-063aff900ca1 h1:Nykk7fggxChwLK4rUPYESzeIwqsuxXXlFEAh5YhaMRo= -mvdan.cc/unparam v0.0.0-20240427195214-063aff900ca1/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI= -mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= -mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/.bingo/jb.mod b/.bingo/jb.mod deleted file mode 100644 index 732a8054017..00000000000 --- a/.bingo/jb.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.18 - -require github.com/jsonnet-bundler/jsonnet-bundler v0.5.1 // cmd/jb diff --git a/.bingo/jb.sum b/.bingo/jb.sum deleted file mode 100644 index 59c58ef41f1..00000000000 --- a/.bingo/jb.sum +++ /dev/null @@ -1,50 +0,0 @@ -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/jsonnet-bundler/jsonnet-bundler v0.5.1 h1:eUd6EA1Qzz73Q4NLNLOrNkMb96+6NTTERbX9lqaxVwk= -github.com/jsonnet-bundler/jsonnet-bundler v0.5.1/go.mod h1:Qrdw/7mOFS2SKCOALKFfEH8gdvXJi8XZjw9g5ilpf4I= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.bingo/lefthook.mod b/.bingo/lefthook.mod deleted file mode 100644 index 557c374fd91..00000000000 --- a/.bingo/lefthook.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.20 - -require github.com/evilmartians/lefthook v1.4.8 diff --git a/.bingo/lefthook.sum b/.bingo/lefthook.sum deleted file mode 100644 index 4f9fd11d4c6..00000000000 --- a/.bingo/lefthook.sum +++ /dev/null @@ -1,523 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= -github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= -github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evilmartians/lefthook v1.4.8 h1:8FmXWtfFiEZw3w18JbhVrp3g+Iy/j2XEo6gcC25+4KA= -github.com/evilmartians/lefthook v1.4.8/go.mod h1:anwwu2QiCEnsOCBHfRgGOB3/sd9FMVNhmY8l9DDQAG8= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= -github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -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= -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= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -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= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM= -gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/.bingo/swagger.mod b/.bingo/swagger.mod deleted file mode 100644 index ef5b4b6e2c6..00000000000 --- a/.bingo/swagger.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.18 - -require github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // cmd/swagger diff --git a/.bingo/swagger.sum b/.bingo/swagger.sum deleted file mode 100644 index 0dc152e6e87..00000000000 --- a/.bingo/swagger.sum +++ /dev/null @@ -1,810 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= -github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= -github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= -github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= -github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk= -github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.21.1 h1:/KIG00BzA2x2HRStX2tnhbqbQdPcFlkgsYCiNY20FZs= -github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo= -github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= -github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw= -github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= -github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-swagger/go-swagger v0.29.0 h1:z3YoZtLvS1Y8TE/PCat1VypcZxM0IgKLt0NvZxQyNl8= -github.com/go-swagger/go-swagger v0.29.0/go.mod h1:Z4GJzI+bHKKkGB2Ji1rawpi3/ldXX8CkzGIa9HAC5EE= -github.com/go-swagger/go-swagger v0.30.2 h1:23odPUyQZdkNFZZSBJ3mqYYcdh+LnuReEbdWN18OMRo= -github.com/go-swagger/go-swagger v0.30.2/go.mod h1:neDPes8r8PCz2JPvHRDj8BTULLh4VJUt7n6MpQqxhHM= -github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo= -github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I= -github.com/go-swagger/go-swagger v0.30.6-0.20240418033037-c46c303aaa02 h1:J6YiT/eg3gAfKMdVCkWXe6khsO+nxa8W4URZ4AUqzbA= -github.com/go-swagger/go-swagger v0.30.6-0.20240418033037-c46c303aaa02/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -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/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= -github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= -github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/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.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.8.2 h1:8ssUXufb90ujcIvR6MyE1SchaNj0SFxsakiZgxIyrMk= -go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4= -go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= -go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba h1:6u6sik+bn/y7vILcYkK3iwTBWN7WtBvB0+SZswQnbf8= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -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= -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= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -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= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/.bingo/variables.env b/.bingo/variables.env index 03b44adfca5..b4e2c715f3e 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -8,19 +8,5 @@ if [ -z "$GOBIN" ]; then fi -BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8" - -COG="${GOBIN}/cog-v0.0.15" - -CUE="${GOBIN}/cue-v0.5.0" - DRONE="${GOBIN}/drone-v1.5.0" -GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.62.0" - -JB="${GOBIN}/jb-v0.5.1" - -LEFTHOOK="${GOBIN}/lefthook-v1.4.8" - -SWAGGER="${GOBIN}/swagger-v0.30.6-0.20240310114303-db51e79a0e37" - diff --git a/.prettierignore b/.prettierignore index 23d9231bc49..b9f59a37afb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,7 @@ .git .github .yarn +.bingo build compiled data From 5bb1d5f2c37514f19760a888e638bf9daf51e480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20V=C4=93rzemnieks?= Date: Mon, 24 Mar 2025 11:37:19 +0100 Subject: [PATCH 22/86] Cloudwatch: Migrate to aws-sdk-go-v2 (#99643) --- go.mod | 40 +-- go.sum | 78 +++--- go.work.sum | 8 + pkg/build/go.mod | 2 +- pkg/build/go.sum | 4 +- pkg/storage/unified/apistore/go.mod | 34 +-- pkg/storage/unified/apistore/go.sum | 68 ++--- pkg/storage/unified/resource/go.mod | 34 +-- pkg/storage/unified/resource/go.sum | 68 ++--- pkg/tsdb/cloudwatch/annotation_query.go | 36 +-- pkg/tsdb/cloudwatch/annotation_query_test.go | 17 +- pkg/tsdb/cloudwatch/client_factory.go | 57 ++--- pkg/tsdb/cloudwatch/clients/metrics.go | 55 ++-- pkg/tsdb/cloudwatch/clients/metrics_test.go | 21 +- pkg/tsdb/cloudwatch/cloudwatch.go | 204 +++++++-------- .../cloudwatch/cloudwatch_integration_test.go | 110 ++++---- pkg/tsdb/cloudwatch/cloudwatch_test.go | 80 +++--- .../get_dimension_values_for_wildcards.go | 3 +- ...get_dimension_values_for_wildcards_test.go | 43 ++-- .../cloudwatch/get_metric_data_executor.go | 11 +- .../get_metric_data_executor_test.go | 30 ++- pkg/tsdb/cloudwatch/log_actions.go | 129 ++++------ pkg/tsdb/cloudwatch/log_actions_test.go | 241 +++++++----------- pkg/tsdb/cloudwatch/log_query.go | 48 ++-- pkg/tsdb/cloudwatch/log_query_test.go | 96 +++---- pkg/tsdb/cloudwatch/log_sync_query.go | 8 +- pkg/tsdb/cloudwatch/log_sync_query_test.go | 106 +++----- .../cloudwatch/metric_data_input_builder.go | 9 +- .../metric_data_input_builder_test.go | 9 +- .../cloudwatch/metric_data_query_builder.go | 27 +- .../metric_data_query_builder_test.go | 5 +- pkg/tsdb/cloudwatch/metric_find_query.go | 84 +++--- pkg/tsdb/cloudwatch/metric_find_query_test.go | 96 ++++--- .../cloudwatch/mocks/cloudwatch_metric_api.go | 78 +++--- pkg/tsdb/cloudwatch/mocks/logs.go | 35 ++- pkg/tsdb/cloudwatch/mocks/metrics_client.go | 5 +- pkg/tsdb/cloudwatch/mocks/oam_client.go | 7 +- pkg/tsdb/cloudwatch/mocks/regions.go | 14 +- pkg/tsdb/cloudwatch/models/api.go | 63 +++-- .../cloudwatch/models/cloudwatch_query.go | 29 ++- .../models/cloudwatch_query_test.go | 12 +- pkg/tsdb/cloudwatch/models/logs_query.go | 2 +- .../cloudwatch/models/query_row_response.go | 24 +- .../resources/log_groups_resource_request.go | 8 +- pkg/tsdb/cloudwatch/models/resources/types.go | 6 +- pkg/tsdb/cloudwatch/response_parser.go | 16 +- pkg/tsdb/cloudwatch/response_parser_test.go | 220 ++++++++-------- .../cloudwatch/routes/log_group_fields.go | 2 +- .../routes/log_group_fields_test.go | 4 +- pkg/tsdb/cloudwatch/routes/log_groups.go | 2 +- pkg/tsdb/cloudwatch/routes/log_groups_test.go | 34 +-- pkg/tsdb/cloudwatch/services/accounts.go | 23 +- pkg/tsdb/cloudwatch/services/accounts_test.go | 67 ++--- pkg/tsdb/cloudwatch/services/list_metrics.go | 32 +-- .../cloudwatch/services/list_metrics_test.go | 28 +- pkg/tsdb/cloudwatch/services/log_groups.go | 20 +- .../cloudwatch/services/log_groups_test.go | 149 +++++------ pkg/tsdb/cloudwatch/services/regions.go | 12 +- pkg/tsdb/cloudwatch/services/regions_test.go | 12 +- pkg/tsdb/cloudwatch/sort_frame_test.go | 2 +- pkg/tsdb/cloudwatch/test_utils.go | 223 ++++++++-------- pkg/tsdb/cloudwatch/time_series_query_test.go | 124 ++++----- 62 files changed, 1497 insertions(+), 1617 deletions(-) diff --git a/go.mod b/go.mod index ae380cc710d..63ce71d455a 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,13 @@ require ( github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // @grafana/plugins-platform-backend github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad - github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2 v1.36.1 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 // @grafana/aws-datasources + github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad @@ -85,7 +91,7 @@ require ( github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend github.com/grafana/grafana-app-sdk v0.31.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.30.0 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources + github.com/grafana/grafana-aws-sdk v0.33.0 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources @@ -221,6 +227,8 @@ require ( github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250317130411-3f270d1de043 // @grafana/grafana-search-and-storage ) +require github.com/aws/smithy-go v1.22.2 // @grafana/aws-datasources + require ( cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect @@ -264,25 +272,23 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -372,7 +378,7 @@ require ( github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.3 // indirect + github.com/grafana/sqlds/v4 v4.1.7 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/grafana-search-and-storage github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/hashicorp/consul/api v1.30.0 // indirect diff --git a/go.sum b/go.sum index 82affcd7be1..3e27d6a9642 100644 --- a/go.sum +++ b/go.sum @@ -839,46 +839,56 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= -github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= -github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= +github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= +github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 h1:bfHEPSWRqKAUp9ugaYDo6bYmCwYGhpGlcSYbnjpZ4lQ= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9/go.mod h1:w0Sa1DOIjqTBXmwYFk1r+i6Xtkeq21JGjUGe/NCqBHs= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 h1:DddWiL/XVT9GjMZqbYoIpJm5fFa08/CSk7fPN5neWVY= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7/go.mod h1:zZeYjS1D+qvIOiDrCT89Rrm6vSn4m8DNhi0kb3wwzYM= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 h1:3hH6o7Z2WeE1twvz44Aitn6Qz8DZN3Dh5IB4Eh2xq7s= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0/go.mod h1:I76S7jN0nfsYTBtuTgTsJtK2Q8yJVDgrLr5eLN64wMA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= +github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 h1:4IrWw0hu8HEdo+6htGgzrjiiTJeyreyofj1SEZDb5qc= +github.com/aws/aws-sdk-go-v2/service/oam v1.15.13/go.mod h1:nUyUC5TvB8cwPPZjwCkJ6+NsT6TnJCEiLreXDdRQchU= +github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 h1:wdMzCMpoSKRYp4vtciAxPzjJy7wSEQsl0pkvlAJQ+Xo= +github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13/go.mod h1:jhfb2oFQrEqsl6AqYkFlhz1kUys4AWXaFzfA1BCzYWY= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= @@ -1569,8 +1579,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= -github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= +github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= +github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY= @@ -1627,8 +1637,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 h1:SDGrP81Vcd102L3UJEryRd1eestRw73wt+b8vnVEFe0= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= -github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= -github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= +github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= +github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 h1:XMreZ1SPjLpd9zhql5FXKFYwAcgBzS2E2MOPx4n+FyY= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2/go.mod h1:UKONJhBCxmL+0ri27VMledCVzZIJqnl6Ah24A5vCRzs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= diff --git a/go.work.sum b/go.work.sum index dfa0340d910..9c6ea83ad05 100644 --- a/go.work.sum +++ b/go.work.sum @@ -848,6 +848,7 @@ github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6 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 v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v1.7.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/elazarl/goproxy v1.7.1/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= @@ -989,7 +990,10 @@ github.com/grafana/cog v0.0.23 h1:/0CCJ24Z8XXM2DnboSd2FzoIswUroqIZzVr8oJWmMQs= github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= +github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= +github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= +github.com/grafana/grafana-plugin-sdk-go v0.266.0/go.mod h1:bxkXrBQ4QSmOncsWdIOcpgP+M6wajQNMAPXlbWrqAWY= github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q= github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= @@ -1007,6 +1011,7 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= @@ -1141,6 +1146,7 @@ github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= github.com/matryer/moq v0.3.3 h1:pScMH9VyrdT4S93yiLpVyU8rCDqGQr24uOyBxmktG5Q= github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M= @@ -1160,6 +1166,7 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -1667,6 +1674,7 @@ golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 14352adba3e..ed6f08c804c 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -10,7 +10,7 @@ replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible require ( cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad - github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources github.com/docker/docker v27.5.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 1d55ac2a88b..55b8c06217d 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -53,8 +53,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index c6b1c75bfda..861b134152f 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -76,26 +76,26 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go v1.55.5 // indirect - github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -210,7 +210,7 @@ require ( github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.31.5 // indirect + github.com/grafana/grafana-aws-sdk v0.33.0 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/grafana-plugin-sdk-go v0.272.0 // indirect github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // indirect @@ -219,7 +219,7 @@ require ( github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.3 // indirect + github.com/grafana/sqlds/v4 v4.1.7 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index d2ab1f07f14..c1c61c2582c 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -735,46 +735,46 @@ github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= -github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= -github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= +github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= +github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -1265,8 +1265,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= -github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= +github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= +github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1285,8 +1285,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= -github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= +github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= +github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index e3aab925836..354a247aee9 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -60,26 +60,26 @@ require ( github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.55.5 // indirect - github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bluele/gcache v0.0.2 // indirect @@ -136,11 +136,11 @@ require ( github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.31.5 // indirect + github.com/grafana/grafana-aws-sdk v0.33.0 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect - github.com/grafana/sqlds/v4 v4.1.3 // indirect + github.com/grafana/sqlds/v4 v4.1.7 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 2ac29e1c27f..62654cc32e4 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -714,46 +714,46 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ= github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= -github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= -github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= +github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= +github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -1160,8 +1160,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= -github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= +github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= +github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1172,8 +1172,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF 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= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= -github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= +github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= +github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog= diff --git a/pkg/tsdb/cloudwatch/annotation_query.go b/pkg/tsdb/cloudwatch/annotation_query.go index 2eb61676354..be38f571726 100644 --- a/pkg/tsdb/cloudwatch/annotation_query.go +++ b/pkg/tsdb/cloudwatch/annotation_query.go @@ -7,8 +7,10 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -29,14 +31,14 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC statistic = *model.Statistic } - var period int64 + var period int32 if model.Period != nil && *model.Period != "" { - p, err := strconv.ParseInt(*model.Period, 10, 64) + p, err := strconv.ParseInt(*model.Period, 10, 32) if err != nil { return nil, backend.DownstreamError(fmt.Errorf("query period must be an int")) } - period = p + period = int32(p) } prefixMatching := false @@ -69,11 +71,11 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC if prefixMatching { params := &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int64(100), + MaxRecords: aws.Int32(100), ActionPrefix: actionPrefix, AlarmNamePrefix: alarmNamePrefix, } - resp, err := cli.DescribeAlarms(params) + resp, err := cli.DescribeAlarms(ctx, params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarms", err))) return result, nil @@ -84,10 +86,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC return result, backend.DownstreamError(errors.New("invalid annotations query")) } - var qd []*cloudwatch.Dimension + var qd []cloudwatchtypes.Dimension for k, v := range dimensions { for _, vvv := range v.ArrayOfString { - qd = append(qd, &cloudwatch.Dimension{ + qd = append(qd, cloudwatchtypes.Dimension{ Name: aws.String(k), Value: aws.String(vvv), }) @@ -97,10 +99,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC Namespace: aws.String(model.Namespace), MetricName: aws.String(metricName), Dimensions: qd, - Statistic: aws.String(statistic), - Period: aws.Int64(period), + Statistic: cloudwatchtypes.Statistic(statistic), + Period: aws.Int32(period), } - resp, err := cli.DescribeAlarmsForMetric(params) + resp, err := cli.DescribeAlarmsForMetric(ctx, params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmsForMetric", err))) return result, nil @@ -116,9 +118,9 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC AlarmName: alarmName, StartDate: aws.Time(query.TimeRange.From), EndDate: aws.Time(query.TimeRange.To), - MaxRecords: aws.Int64(100), + MaxRecords: aws.Int32(100), } - resp, err := cli.DescribeAlarmHistory(params) + resp, err := cli.DescribeAlarmHistory(ctx, params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmHistory", err))) return result, nil @@ -127,7 +129,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC annotations = append(annotations, &annotationEvent{ Time: *history.Timestamp, Title: *history.AlarmName, - Tags: *history.HistoryItemType, + Tags: string(history.HistoryItemType), Text: *history.HistorySummary, }) } @@ -162,7 +164,7 @@ func transformAnnotationToTable(annotations []*annotationEvent, query backend.Da } func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, metricName string, - dimensions dataquery.Dimensions, statistic string, period int64) []*string { + dimensions dataquery.Dimensions, statistic string, period int32) []*string { alarmNames := make([]*string, 0) for _, alarm := range alarms.MetricAlarms { @@ -189,7 +191,7 @@ func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, met continue } - if *alarm.Statistic != statistic { + if string(alarm.Statistic) != statistic { continue } diff --git a/pkg/tsdb/cloudwatch/annotation_query_test.go b/pkg/tsdb/cloudwatch/annotation_query_test.go index 6779c82a896..9dd77fb1f3b 100644 --- a/pkg/tsdb/cloudwatch/annotation_query_test.go +++ b/pkg/tsdb/cloudwatch/annotation_query_test.go @@ -5,12 +5,13 @@ import ( "encoding/json" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,7 +23,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { }) var client fakeCWAnnotationsClient - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &client } @@ -53,8 +54,8 @@ func TestQuery_AnnotationQuery(t *testing.T) { assert.Equal(t, &cloudwatch.DescribeAlarmsForMetricInput{ Namespace: aws.String("custom"), MetricName: aws.String("CPUUtilization"), - Statistic: aws.String("Average"), - Period: aws.Int64(300), + Statistic: "Average", + Period: aws.Int32(300), }, client.calls.describeAlarmsForMetric[0]) }) @@ -86,7 +87,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { require.Len(t, client.calls.describeAlarms, 1) assert.Equal(t, &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int64(100), + MaxRecords: aws.Int32(100), ActionPrefix: aws.String("some_action_prefix"), AlarmNamePrefix: aws.String("some_alarm_name_prefix"), }, client.calls.describeAlarms[0]) diff --git a/pkg/tsdb/cloudwatch/client_factory.go b/pkg/tsdb/cloudwatch/client_factory.go index 258640e7f80..2a683e7772a 100644 --- a/pkg/tsdb/cloudwatch/client_factory.go +++ b/pkg/tsdb/cloudwatch/client_factory.go @@ -1,64 +1,53 @@ package cloudwatch import ( - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/oam" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/oam" + "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) -// NewMetricsAPI is a CloudWatch metrics api factory. +// NewCWClient is a CloudWatch metrics api factory. // // Stubbable by tests. -var NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { - return cloudwatch.New(sess) +var NewCWClient = func(cfg aws.Config) models.CWClient { + return cloudwatch.NewFromConfig(cfg) } // NewLogsAPI is a CloudWatch logs api factory. // // Stubbable by tests. -var NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { - return cloudwatchlogs.New(sess) +var NewLogsAPI = func(cfg aws.Config) models.CloudWatchLogsAPIProvider { + return cloudwatchlogs.NewFromConfig(cfg) } -// NewOAMAPI is a CloudWatch OAM api factory. +// NewOAMAPI is a CloudWatch OAM API factory // // Stubbable by tests. -var NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { - return oam.New(sess) +var NewOAMAPI = func(cfg aws.Config) models.OAMAPIProvider { + return oam.NewFromConfig(cfg) } -// NewCWClient is a CloudWatch client factory. +// NewEC2API is a CloudWatch EC2 API factory // -// Stubbable by tests. -var NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { - return cloudwatch.New(sess) +// Stubbable by tests +var NewEC2API = func(cfg aws.Config) models.EC2APIProvider { + return ec2.NewFromConfig(cfg) } // NewCWLogsClient is a CloudWatch logs client factory. // // Stubbable by tests. -var NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { - return cloudwatchlogs.New(sess) -} - -// NewEC2Client is a client factory. -// -// Stubbable by tests. -var NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { - return ec2.New(provider) +var NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + return cloudwatchlogs.NewFromConfig(cfg) } -// RGTA client factory. +// NewRGTAClient is a ResourceGroupsTaggingAPI Client factory. // // Stubbable by tests. -var newRGTAClient = func(provider client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { - return resourcegroupstaggingapi.New(provider) +var NewRGTAClient = func(cfg aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { + return resourcegroupstaggingapi.NewFromConfig(cfg) } diff --git a/pkg/tsdb/cloudwatch/clients/metrics.go b/pkg/tsdb/cloudwatch/clients/metrics.go index b3648492a59..d3f46606f8e 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics.go +++ b/pkg/tsdb/cloudwatch/clients/metrics.go @@ -3,41 +3,42 @@ package clients import ( "context" - "github.com/aws/aws-sdk-go/aws/awsutil" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -// this client wraps the CloudWatch API and handles pagination and the composition of the MetricResponse DTO -type metricsClient struct { - models.CloudWatchMetricsAPIProvider +type MetricsClient struct { + cloudwatch.ListMetricsAPIClient + listMetricsPageLimit int } -func NewMetricsClient(api models.CloudWatchMetricsAPIProvider, pageLimit int) *metricsClient { - return &metricsClient{CloudWatchMetricsAPIProvider: api, listMetricsPageLimit: pageLimit} +func NewMetricsClient(client cloudwatch.ListMetricsAPIClient, listMetricsPageLimit int) *MetricsClient { + return &MetricsClient{ + ListMetricsAPIClient: client, + listMetricsPageLimit: listMetricsPageLimit, + } } -func (l *metricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { - var cloudWatchMetrics []resources.MetricResponse - pageNum := 0 - err := l.ListMetricsPagesWithContext(ctx, params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { - pageNum++ - utils.QueriesTotalCounter.WithLabelValues(utils.ListMetricsLabel).Inc() - metrics, err := awsutil.ValuesAtPath(page, "Metrics") - if err == nil { - for idx, metric := range metrics { - metric := resources.MetricResponse{Metric: metric.(*cloudwatch.Metric)} - if len(page.OwningAccounts) >= idx && params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts { - metric.AccountId = page.OwningAccounts[idx] - } - cloudWatchMetrics = append(cloudWatchMetrics, metric) +func (mc *MetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { + var response []resources.MetricResponse + paginator := cloudwatch.NewListMetricsPaginator(mc.ListMetricsAPIClient, params) + includeAccount := params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts + pages := 0 + for paginator.HasMorePages() && pages < mc.listMetricsPageLimit { + pages += 1 + page, err := paginator.NextPage(ctx) + if err != nil { + return response, err + } + for i, metric := range page.Metrics { + resp := resources.MetricResponse{Metric: metric} + if includeAccount && len(page.OwningAccounts) >= i { + resp.AccountId = &page.OwningAccounts[i] } + response = append(response, resp) } - return !lastPage && pageNum < l.listMetricsPageLimit - }) - - return cloudWatchMetrics, err + } + return response, nil } diff --git a/pkg/tsdb/cloudwatch/clients/metrics_test.go b/pkg/tsdb/cloudwatch/clients/metrics_test.go index 644aecf85e4..7f221cc8247 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics_test.go +++ b/pkg/tsdb/cloudwatch/clients/metrics_test.go @@ -4,16 +4,19 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMetricsClient(t *testing.T) { - metrics := []*cloudwatch.Metric{ + metrics := []cloudwatchtypes.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, @@ -50,25 +53,25 @@ func TestMetricsClient(t *testing.T) { }) t.Run("Should return account id in case IncludeLinkedAccounts is set to true", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, - }, OwningAccounts: []*string{aws.String("1234567890"), aws.String("1234567890"), aws.String("1234567895")}} + }, OwningAccounts: []string{"1234567890", "1234567890", "1234567895"}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(true)}) require.NoError(t, err) expected := []resources.MetricResponse{ - {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, - {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, - {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, + {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, + {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, + {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, } assert.Equal(t, expected, response) }) t.Run("Should not return account id in case IncludeLinkedAccounts is set to false", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []*string{aws.String("1234567890")}} + fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []string{"1234567890"}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(false)}) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 74ae89e7748..88b96836ff9 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -3,18 +3,17 @@ package cloudwatch import ( "context" "encoding/json" - "errors" "fmt" - "net/http" + "slices" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + + "github.com/grafana/grafana-aws-sdk/pkg/awsauth" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -37,6 +36,13 @@ const ( // headerFromAlert is used by datasources to identify alert queries headerFromAlert = "FromAlert" + + defaultRegion = "default" + logsQueryMode = "Logs" + // QueryTypes + annotationQuery = "annotationQuery" + logAction = "logAction" + timeSeriesQuery = "timeSeriesQuery" ) type DataQueryJson struct { @@ -45,21 +51,39 @@ type DataQueryJson struct { } type DataSource struct { - Settings models.CloudWatchSettings - HTTPClient *http.Client - sessions SessionCache + Settings models.CloudWatchSettings + ProxyOpts *proxy.Options + AWSConfigProvider awsauth.ConfigProvider + tagValueCache *cache.Cache - ProxyOpts *proxy.Options } -const ( - defaultRegion = "default" - logsQueryMode = "Logs" - // QueryTypes - annotationQuery = "annotationQuery" - logAction = "logAction" - timeSeriesQuery = "timeSeriesQuery" -) +func (ds *DataSource) newAWSConfig(ctx context.Context, region string) (aws.Config, error) { + if region == defaultRegion { + if len(ds.Settings.Region) == 0 { + return aws.Config{}, models.ErrMissingRegion + } + region = ds.Settings.Region + } + authSettings := awsauth.Settings{ + CredentialsProfile: ds.Settings.Profile, + LegacyAuthType: ds.Settings.AuthType, + AssumeRoleARN: ds.Settings.AssumeRoleARN, + ExternalID: ds.Settings.GrafanaSettings.ExternalID, + Endpoint: ds.Settings.Endpoint, + Region: region, + AccessKey: ds.Settings.AccessKey, + SecretKey: ds.Settings.SecretKey, + } + if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { + authSettings.ProxyOptions = ds.ProxyOpts + } + cfg, err := ds.AWSConfigProvider.GetConfig(ctx, authSettings) + if err != nil { + return aws.Config{}, err + } + return cfg, nil +} func ProvideService(httpClientProvider *httpclient.Provider) *CloudWatchService { logger := backend.NewLoggerWith("logger", "tsdb.cloudwatch") @@ -80,7 +104,7 @@ type CloudWatchService struct { } type SessionCache interface { - GetSessionWithAuthSettings(c awsds.GetSessionConfig, as awsds.AuthSettings) (*session.Session, error) + CredentialsProviderV2(ctx context.Context, cfg awsds.GetSessionConfig) (aws.CredentialsProvider, error) } func newExecutor(im instancemgmt.InstanceManager, logger log.Logger) *cloudWatchExecutor { @@ -105,18 +129,12 @@ func NewInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins return nil, err } - httpClient, err := httpClientProvider.New(opts) - if err != nil { - return nil, fmt.Errorf("error creating http client: %w", err) - } - return DataSource{ Settings: instanceSettings, - HTTPClient: httpClient, tagValueCache: cache.New(tagValueCacheExpiration, tagValueCacheExpiration*5), - sessions: awsds.NewSessionCache(), // this is used to build a custom dialer when secure socks proxy is enabled - ProxyOpts: opts.ProxyOptions, + ProxyOpts: opts.ProxyOptions, + AWSConfigProvider: awsauth.NewConfigProvider(), }, nil } } @@ -145,30 +163,31 @@ func instrumentContext(ctx context.Context, endpoint string, pCtx backend.Plugin } func (e *cloudWatchExecutor) getRequestContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.RequestContext, error) { - r := region instance, err := e.getInstance(ctx, pluginCtx) + if err != nil { + return models.RequestContext{}, err + } + if region == defaultRegion { - if err != nil { - return models.RequestContext{}, err - } - r = instance.Settings.Region + region = instance.Settings.Region } - ec2Client, err := e.getEC2Client(ctx, pluginCtx, defaultRegion) + cfg, err := instance.newAWSConfig(ctx, defaultRegion) if err != nil { return models.RequestContext{}, err } + ec2client := NewEC2API(cfg) - sess, err := instance.newSession(r) + cfg, err = instance.newAWSConfig(ctx, region) if err != nil { return models.RequestContext{}, err } return models.RequestContext{ - OAMAPIProvider: NewOAMAPI(sess), - MetricsClientProvider: clients.NewMetricsClient(NewMetricsAPI(sess), instance.Settings.GrafanaSettings.ListMetricsPageLimit), - LogsAPIProvider: NewLogsAPI(sess), - EC2APIProvider: ec2Client, + OAMAPIProvider: NewOAMAPI(cfg), + MetricsClientProvider: clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit), + LogsAPIProvider: NewLogsAPI(cfg), + EC2APIProvider: ec2client, Settings: instance.Settings, Logger: e.logger.FromContext(ctx), }, nil @@ -272,86 +291,32 @@ func (e *cloudWatchExecutor) checkHealthMetrics(ctx context.Context, pluginCtx b return err } - session, err := instance.newSession(defaultRegion) + cfg, err := instance.newAWSConfig(ctx, defaultRegion) if err != nil { return err } - metricClient := clients.NewMetricsClient(NewMetricsAPI(session), instance.Settings.GrafanaSettings.ListMetricsPageLimit) + metricClient := clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit) _, err = metricClient.ListMetricsWithPageLimit(ctx, params) return err } func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error { - session, err := e.newSessionFromContext(ctx, pluginCtx, defaultRegion) + cfg, err := e.getAWSConfig(ctx, pluginCtx, defaultRegion) if err != nil { return err } - logsClient := NewLogsAPI(session) - _, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(1)}) + logsClient := NewLogsAPI(cfg) + _, err = logsClient.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(1)}) return err } -func (ds *DataSource) newSession(region string) (*session.Session, error) { - if region == defaultRegion { - if len(ds.Settings.Region) == 0 { - return nil, models.ErrMissingRegion - } - region = ds.Settings.Region - } - sess, err := ds.sessions.GetSessionWithAuthSettings(awsds.GetSessionConfig{ - // https://github.com/grafana/grafana/issues/46365 - // HTTPClient: instance.HTTPClient, - Settings: awsds.AWSDatasourceSettings{ - Profile: ds.Settings.Profile, - Region: region, - AuthType: ds.Settings.AuthType, - AssumeRoleARN: ds.Settings.AssumeRoleARN, - ExternalID: ds.Settings.ExternalID, - Endpoint: ds.Settings.Endpoint, - DefaultRegion: ds.Settings.Region, - AccessKey: ds.Settings.AccessKey, - SecretKey: ds.Settings.SecretKey, - }, - UserAgentName: aws.String("Cloudwatch")}, - ds.Settings.GrafanaSettings) - if err != nil { - return nil, err - } - - // work around until https://github.com/grafana/grafana/issues/39089 is implemented - if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { - // only update the transport to try to avoid the issue mentioned here https://github.com/grafana/grafana/issues/46365 - // also, 'sess' is cached and reused, so the first time it might have the transport not set, the following uses it will - if sess.Config.HTTPClient.Transport == nil { - // following go standard library logic (https://pkg.go.dev/net/http#Client), if no Transport is provided, - // then we use http.DefaultTransport - defTransport, ok := http.DefaultTransport.(*http.Transport) - if !ok { - // this should not happen but validating just in case - return nil, errors.New("default http client transport is not of type http.Transport") - } - sess.Config.HTTPClient.Transport = defTransport.Clone() - } - err = proxy.New(ds.ProxyOpts).ConfigureSecureSocksHTTPProxy(sess.Config.HTTPClient.Transport.(*http.Transport)) - if err != nil { - return nil, fmt.Errorf("error configuring Secure Socks proxy for Transport: %w", err) - } - } else if sess.Config.HTTPClient != nil { - // Workaround for https://github.com/grafana/grafana/issues/91356 - PDC transport set above - // stays on the cached session after PDC is disabled - sess.Config.HTTPClient.Transport = nil - } - return sess, nil -} - -func (e *cloudWatchExecutor) newSessionFromContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (*session.Session, error) { +func (e *cloudWatchExecutor) getAWSConfig(ctx context.Context, pluginCtx backend.PluginContext, region string) (aws.Config, error) { instance, err := e.getInstance(ctx, pluginCtx) if err != nil { - return nil, err + return aws.Config{}, err } - - return instance.newSession(region) + return instance.newAWSConfig(ctx, region) } func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*DataSource, error) { @@ -364,44 +329,51 @@ func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend. return &instance, nil } -func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchiface.CloudWatchAPI, error) { - sess, err := e.newSessionFromContext(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWClient, error) { + cfg, err := e.getAWSConfig(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewCWClient(sess), nil + return NewCWClient(cfg), nil } -func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchlogsiface.CloudWatchLogsAPI, error) { - sess, err := e.newSessionFromContext(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWLogsClient, error) { + cfg, err := e.getAWSConfig(ctx, pluginCtx, region) if err != nil { return nil, err } - logsClient := NewCWLogsClient(sess) + logsClient := NewCWLogsClient(cfg) return logsClient, nil } func (e *cloudWatchExecutor) getEC2Client(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.EC2APIProvider, error) { - sess, err := e.newSessionFromContext(ctx, pluginCtx, region) + cfg, err := e.getAWSConfig(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewEC2Client(sess), nil + return NewEC2API(cfg), nil } -func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI, +func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapi.GetResourcesAPIClient, error) { - sess, err := e.newSessionFromContext(ctx, pluginCtx, region) + cfg, err := e.getAWSConfig(ctx, pluginCtx, region) if err != nil { return nil, err } - return newRGTAClient(sess), nil + return NewRGTAClient(cfg), nil +} + +var terminatedStates = []cloudwatchlogstypes.QueryStatus{ + cloudwatchlogstypes.QueryStatusComplete, + cloudwatchlogstypes.QueryStatusCancelled, + cloudwatchlogstypes.QueryStatusFailed, + cloudwatchlogstypes.QueryStatusTimeout, } -func isTerminated(queryStatus string) bool { - return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout" +func isTerminated(queryStatus cloudwatchlogstypes.QueryStatus) bool { + return slices.Contains(terminatedStates, queryStatus) } diff --git a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go index e9ce1632e21..7b0cfcf75b9 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go @@ -6,12 +6,12 @@ import ( "net/http" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -27,46 +27,46 @@ import ( func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewMetricsAPI := NewMetricsAPI + origNewCWClient := NewCWClient origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2Client := NewEC2Client - NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } + origNewEC2API := NewEC2API + NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } var logApi mocks.LogsAPI - NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { return &logApi } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) - NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { + ec2Mock.On("DescribeRegions", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) + NewEC2API = func(aws.Config) models.EC2APIProvider { return ec2Mock } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewMetricsAPI = origNewMetricsAPI + NewCWClient = origNewCWClient NewLogsAPI = origNewLogsAPI - NewEC2Client = origNewEC2Client + NewEC2API = origNewEC2API }) var api mocks.FakeMetricsAPI - NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { + NewCWClient = func(aws.Config) models.CWClient { return &api } t.Run("Should handle dimension value request and return values from the api", func(t *testing.T) { - im := testInstanceManager(100) - api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, + im := testInstanceManager(100, false) + api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, }, MetricsPerPage: 100} executor := newExecutor(im, log.NewNullLogger()) @@ -91,18 +91,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle dimension key filter query and return keys from the api", func(t *testing.T) { - im := testInstanceManager(3) - api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3, false) + api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -177,18 +177,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle custom namespace metrics query and return metrics from api", func(t *testing.T) { - im := testInstanceManager(3) - api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3, false) + api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -215,15 +215,15 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { t.Run("Should handle log group fields request", func(t *testing.T) { im := defaultTestInstanceManager() logApi = mocks.LogsAPI{} - logApi.On("GetLogGroupFieldsWithContext", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []*cloudwatchlogs.LogGroupField{ + logApi.On("GetLogGroupFields", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ + LogGroupFields: []cloudwatchlogstypes.LogGroupField{ { Name: aws.String("field1"), - Percent: aws.Int64(50), + Percent: 50, }, { Name: aws.String("field2"), - Percent: aws.Int64(50), + Percent: 50, }, }, }, nil) diff --git a/pkg/tsdb/cloudwatch/cloudwatch_test.go b/pkg/tsdb/cloudwatch/cloudwatch_test.go index 5e36adcda4c..e7a2c163bb9 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - awsclient "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -114,21 +114,21 @@ func TestNewInstanceSettings(t *testing.T) { } func Test_CheckHealth(t *testing.T) { - origNewMetricsAPI := NewMetricsAPI + origNewCWClient := NewCWClient origNewCWLogsClient := NewCWLogsClient origNewLogsAPI := NewLogsAPI t.Cleanup(func() { - NewMetricsAPI = origNewMetricsAPI + NewCWClient = origNewCWClient NewCWLogsClient = origNewCWLogsClient NewLogsAPI = origNewLogsAPI }) var client fakeCheckHealthClient - NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { + NewCWClient = func(aws.Config) models.CWClient { return client } - NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { return client } im := defaultTestInstanceManager() @@ -138,8 +138,7 @@ func Test_CheckHealth(t *testing.T) { executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{ - PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, - }) + PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}}) assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ @@ -150,7 +149,7 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) { client = fakeCheckHealthClient{ - describeLogGroups: func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + describeLogGroupsFunction: func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { return nil, fmt.Errorf("some logs query error") }} @@ -169,8 +168,8 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries logs, fails during metrics query", func(t *testing.T) { client = fakeCheckHealthClient{ - listMetricsPages: func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error { - return fmt.Errorf("some list metrics error") + listMetricsFunction: func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { + return nil, fmt.Errorf("some list metrics error") }} executor := newExecutor(im, log.NewNullLogger()) @@ -188,14 +187,7 @@ func Test_CheckHealth(t *testing.T) { t.Run("fail to get clients", func(t *testing.T) { client = fakeCheckHealthClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{ - Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, - sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { - return nil, fmt.Errorf("some sessions error") - }}, - }, nil - }) + im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, true) executor := newExecutor(im, log.NewNullLogger()) @@ -206,12 +198,14 @@ func Test_CheckHealth(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ Status: backend.HealthStatusError, - Message: "1. CloudWatch metrics query failed: some sessions error\n2. CloudWatch logs query failed: some sessions error", + Message: "1. CloudWatch metrics query failed: LoadDefaultConfig failed\n2. CloudWatch logs query failed: LoadDefaultConfig failed", }, resp) }) } -func TestNewSession_passes_authSettings(t *testing.T) { +func TestGetAWSConfig_passes_authSettings(t *testing.T) { + // TODO: update this for the new auth structure, or remove it + t.Skip() ctxDuration := 15 * time.Minute expectedSettings := awsds.AuthSettings{ AllowedAuthProviders: []string{"foo", "bar", "baz"}, @@ -221,7 +215,7 @@ func TestNewSession_passes_authSettings(t *testing.T) { ListMetricsPageLimit: 50, SecureSocksDSProxyEnabled: true, } - im := datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { return DataSource{ Settings: models.CloudWatchSettings{ AWSDatasourceSettings: awsds.AWSDatasourceSettings{ @@ -229,39 +223,33 @@ func TestNewSession_passes_authSettings(t *testing.T) { }, GrafanaSettings: expectedSettings, }, - sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { - assert.Equal(t, expectedSettings, a) - return &session.Session{ - Config: &aws.Config{}, - }, nil - }}, }, nil - })) + }) executor := newExecutor(im, log.NewNullLogger()) - _, err := executor.newSessionFromContext(context.Background(), + _, err := executor.getAWSConfig(context.Background(), backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, "us-east-1") require.NoError(t, err) } func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewMetricsAPI := NewMetricsAPI + origNewMetricsAPI := NewCWClient origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2Client := NewEC2Client - NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return nil } - NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } - NewEC2Client = func(provider awsclient.ConfigProvider) models.EC2APIProvider { return nil } + origNewEC2API := NewEC2API + NewCWClient = func(aws.Config) models.CWClient { return nil } + NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } + NewEC2API = func(aws.Config) models.EC2APIProvider { return nil } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewMetricsAPI = origNewMetricsAPI + NewCWClient = origNewMetricsAPI NewLogsAPI = origNewLogsAPI - NewEC2Client = origNewEC2Client + NewEC2API = origNewEC2API }) var logsApi mocks.LogsAPI - NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { return &logsApi } @@ -269,8 +257,8 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te t.Run("maps log group api response to resource response of log-groups", func(t *testing.T) { logsApi = mocks.LogsAPI{} - logsApi.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []*cloudwatchlogs.LogGroup{ + logsApi.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ + LogGroups: []cloudwatchlogstypes.LogGroup{ {Arn: aws.String("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: aws.String("group_a")}, }, }, nil) @@ -297,11 +285,11 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te } ]`, string(sender.Response.Body)) - logsApi.AssertCalled(t, "DescribeLogGroupsWithContext", + logsApi.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []*string{utils.Pointer("some-account-id")}, + AccountIdentifiers: []string{"some-account-id"}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: utils.Pointer(int64(50)), + Limit: aws.Int32(50), LogGroupNamePrefix: utils.Pointer("some-pattern"), }) }) diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go index 2919304d84a..ddc281c196f 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/services" + "github.com/patrickmn/go-cache" ) @@ -29,7 +30,7 @@ func shouldSkipFetchingWildcards(ctx context.Context, q *models.CloudWatchQuery) func (e *cloudWatchExecutor) getDimensionValuesForWildcards( ctx context.Context, region string, - client models.CloudWatchMetricsAPIProvider, + client models.CWClient, origQueries []*models.CloudWatchQuery, tagValueCache *cache.Cache, listMetricsPageLimit int, diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go index 5549bc3fc5b..2d8058f2b76 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go @@ -4,7 +4,8 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" @@ -29,10 +30,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, + api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, }} - api.On("ListMetricsPagesWithContext").Return(nil) + api.On("ListMetrics").Return(nil) _, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) // make sure the original query wasn't altered @@ -52,8 +53,8 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName2": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{}} - api.On("ListMetricsPagesWithContext").Return(nil) + api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{}} + api.On("ListMetrics").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -61,10 +62,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { assert.Equal(t, map[string][]string{"Test_DimensionName2": {}}, queries[0].Dimensions) // Confirm that it calls the api again if the last call did not return any values - api.Metrics = []*cloudwatch.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, + api.Metrics = []cloudwatchtypes.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, } - api.On("ListMetricsPagesWithContext").Return(nil) + api.On("ListMetrics").Return(nil) queries, err = executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -132,13 +133,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, - {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, + api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, + {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, }} - api.On("ListMetricsPagesWithContext").Return(nil) + api.On("ListMetrics").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -167,13 +168,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { } query.MetricQueryType = models.MetricQueryTypeQuery - api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, + api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, }} - api.On("ListMetricsPagesWithContext").Return(nil) + api.On("ListMetrics").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor.go b/pkg/tsdb/cloudwatch/get_metric_data_executor.go index 6ad996ab6e1..99395e03452 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor.go @@ -4,15 +4,16 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go-v2/aws" + + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwatchiface.CloudWatchAPI, +func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client models.CWClient, metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) { mdo := make([]*cloudwatch.GetMetricDataOutput, 0) @@ -26,7 +27,7 @@ func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwat *metricDataInput.EndTime = metricDataInput.EndTime.Truncate(time.Minute).Add(time.Minute) } - resp, err := client.GetMetricDataWithContext(ctx, metricDataInput) + resp, err := client.GetMetricData(ctx, metricDataInput) if err != nil { return mdo, backend.DownstreamError(err) } diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go index 3485daff5da..b940fb0f4de 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go @@ -5,8 +5,10 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/stretchr/testify/assert" @@ -18,37 +20,37 @@ func TestGetMetricDataExecutorTestRequest(t *testing.T) { t.Run("Should round up end time if cloudWatchRoundUpEndTime is enabled", func(t *testing.T) { executor := &cloudWatchExecutor{} queryEndTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:45:04Z") - inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{}}}, + MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{}}}, }, nil).Once() _, err := executor.executeRequest(contextWithFeaturesEnabled(features.FlagCloudWatchRoundUpEndTime), mockMetricClient, inputs) require.NoError(t, err) expectedTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:46:00Z") - expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} - mockMetricClient.AssertCalled(t, "GetMetricDataWithContext", mock.Anything, expectedInput, mock.Anything) + expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} + mockMetricClient.AssertCalled(t, "GetMetricData", mock.Anything, expectedInput, mock.Anything) }) } func TestGetMetricDataExecutorTestResponse(t *testing.T) { executor := &cloudWatchExecutor{} - inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []*cloudwatch.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(12.3), aws.Float64(23.5)}}}, + MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{12.3, 23.5}}}, NextToken: aws.String("next"), }, nil).Once() - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(100)}}}, + MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{100}}}, }, nil).Once() res, err := executor.executeRequest(context.Background(), mockMetricClient, inputs) require.NoError(t, err) require.Len(t, res, 2) require.Len(t, res[0].MetricDataResults[0].Values, 2) - assert.Equal(t, 23.5, *res[0].MetricDataResults[0].Values[1]) - assert.Equal(t, 100.0, *res[1].MetricDataResults[0].Values[0]) + assert.Equal(t, 23.5, res[0].MetricDataResults[0].Values[1]) + assert.Equal(t, 100.0, res[1].MetricDataResults[0].Values[0]) } diff --git a/pkg/tsdb/cloudwatch/log_actions.go b/pkg/tsdb/cloudwatch/log_actions.go index 80572895974..fdc197183af 100644 --- a/pkg/tsdb/cloudwatch/log_actions.go +++ b/pkg/tsdb/cloudwatch/log_actions.go @@ -10,11 +10,12 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/smithy-go" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "golang.org/x/sync/errgroup" @@ -25,10 +26,8 @@ import ( ) const ( - limitExceededException = "LimitExceededException" - throttlingException = "ThrottlingException" - defaultEventLimit = int64(10) - defaultLogGroupLimit = int64(50) + defaultEventLimit = int32(10) + defaultLogGroupLimit = int32(50) logIdentifierInternal = "__log__grafana_internal__" logStreamIdentifierInternal = "__logstream__grafana_internal__" ) @@ -43,45 +42,6 @@ func (e *AWSError) Error() string { return fmt.Sprintf("CloudWatch error: %s: %s", e.Code, e.Message) } -// StartQueryInputWithLanguage copies the StartQueryInput struct from aws-sdk-go@v1.55.5 -// (https://github.com/aws/aws-sdk-go/blob/7112c0a0c2d01713a9db2d57f0e5722225baf5b5/service/cloudwatchlogs/api.go#L19541) -// to add support for the new QueryLanguage parameter, which is unlikely to be backported -// since v1 of the aws-sdk-go is in maintenance mode. We've removed the comments for -// clarity. -type StartQueryInputWithLanguage struct { - _ struct{} `type:"structure"` - - EndTime *int64 `locationName:"endTime" type:"long" required:"true"` - Limit *int64 `locationName:"limit" min:"1" type:"integer"` - LogGroupIdentifiers []*string `locationName:"logGroupIdentifiers" type:"list"` - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - LogGroupNames []*string `locationName:"logGroupNames" type:"list"` - QueryString *string `locationName:"queryString" type:"string" required:"true"` - // QueryLanguage is the only change here from the original code. - QueryLanguage *string `locationName:"queryLanguage" type:"string"` - StartTime *int64 `locationName:"startTime" type:"long" required:"true"` -} -type WithQueryLanguageFunc func(language *dataquery.LogsQueryLanguage) func(*request.Request) - -// WithQueryLanguage assigns the function to a variable in order to mock it in log_actions_test.go -var WithQueryLanguage WithQueryLanguageFunc = withQueryLanguage - -func withQueryLanguage(language *dataquery.LogsQueryLanguage) func(request *request.Request) { - return func(request *request.Request) { - sqi := request.Params.(*cloudwatchlogs.StartQueryInput) - request.Params = &StartQueryInputWithLanguage{ - EndTime: sqi.EndTime, - Limit: sqi.Limit, - LogGroupIdentifiers: sqi.LogGroupIdentifiers, - LogGroupName: sqi.LogGroupName, - LogGroupNames: sqi.LogGroupNames, - QueryString: sqi.QueryString, - QueryLanguage: (*string)(language), - StartTime: sqi.StartTime, - } - } -} - func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { resp := backend.NewQueryDataResponse() @@ -168,37 +128,35 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logsQuery mod return data, nil } -func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery) (*data.Frame, error) { limit := defaultEventLimit if logsQuery.Limit != nil && *logsQuery.Limit > 0 { limit = *logsQuery.Limit } - - queryRequest := &cloudwatchlogs.GetLogEventsInput{ - Limit: aws.Int64(limit), - StartFromHead: aws.Bool(logsQuery.StartFromHead), - } - if logsQuery.LogGroupName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logGroupName' is required")) } - queryRequest.SetLogGroupName(logsQuery.LogGroupName) - if logsQuery.LogStreamName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logStreamName' is required")) } - queryRequest.SetLogStreamName(logsQuery.LogStreamName) + + queryRequest := &cloudwatchlogs.GetLogEventsInput{ + Limit: aws.Int32(limit), + StartFromHead: aws.Bool(logsQuery.StartFromHead), + LogGroupName: &logsQuery.LogGroupName, + LogStreamName: &logsQuery.LogStreamName, + } if logsQuery.StartTime != nil && *logsQuery.StartTime != 0 { - queryRequest.SetStartTime(*logsQuery.StartTime) + queryRequest.StartTime = logsQuery.StartTime } if logsQuery.EndTime != nil && *logsQuery.EndTime != 0 { - queryRequest.SetEndTime(*logsQuery.EndTime) + queryRequest.EndTime = logsQuery.EndTime } - logEvents, err := logsClient.GetLogEventsWithContext(ctx, queryRequest) + logEvents, err := logsClient.GetLogEvents(ctx, queryRequest) if err != nil { return nil, backend.DownstreamError(err) } @@ -223,7 +181,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient return data.NewFrame("logEvents", timestampField, messageField), nil } -func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery, timeRange backend.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) { startTime := timeRange.From endTime := timeRange.To @@ -267,34 +225,34 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient c // due to a bug in the startQuery api, we remove * from the arn, otherwise it throws an error logGroupIdentifiers = append(logGroupIdentifiers, strings.TrimSuffix(arn, "*")) } - startQueryInput.LogGroupIdentifiers = aws.StringSlice(logGroupIdentifiers) + startQueryInput.LogGroupIdentifiers = logGroupIdentifiers } else { // even though log group names are being phased out, we still need to support them for backwards compatibility and alert queries - startQueryInput.LogGroupNames = aws.StringSlice(logsQuery.LogGroupNames) + startQueryInput.LogGroupNames = logsQuery.LogGroupNames } } if logsQuery.Limit != nil { - startQueryInput.Limit = aws.Int64(*logsQuery.Limit) + startQueryInput.Limit = aws.Int32(*logsQuery.Limit) + } + if logsQuery.QueryLanguage != nil { + startQueryInput.QueryLanguage = cloudwatchlogstypes.QueryLanguage(*logsQuery.QueryLanguage) } e.logger.FromContext(ctx).Debug("Calling startquery with context with input", "input", startQueryInput) - resp, err := logsClient.StartQueryWithContext(ctx, startQueryInput, WithQueryLanguage(logsQuery.QueryLanguage)) + resp, err := logsClient.StartQuery(ctx, startQueryInput) if err != nil { - var awsErr awserr.Error - if errors.As(err, &awsErr) && awsErr.Code() == "LimitExceededException" { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", awsErr) - err = &AWSError{Code: limitExceededException, Message: err.Error()} - } else if errors.As(err, &awsErr) && awsErr.Code() == "ThrottlingException" { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", awsErr) - err = &AWSError{Code: throttlingException, Message: err.Error()} + if errors.Is(err, &cloudwatchlogstypes.LimitExceededException{}) { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", err) + } else if errors.Is(err, &cloudwatchlogstypes.ThrottlingException{}) { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", err) } err = backend.DownstreamError(err) } return resp, err } -func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery, timeRange backend.TimeRange, refID string) (*data.Frame, error) { startQueryResponse, err := e.executeStartQuery(ctx, logsClient, logsQuery, timeRange) if err != nil { @@ -318,20 +276,19 @@ func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cl return dataFrame, nil } -func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery) (*cloudwatchlogs.StopQueryOutput, error) { queryInput := &cloudwatchlogs.StopQueryInput{ QueryId: aws.String(logsQuery.QueryId), } - response, err := logsClient.StopQueryWithContext(ctx, queryInput) + response, err := logsClient.StopQuery(ctx, queryInput) if err != nil { // If the query has already stopped by the time CloudWatch receives the stop query request, // an "InvalidParameterException" error is returned. For our purposes though the query has been // stopped, so we ignore the error. - var awsErr awserr.Error - if errors.As(err, &awsErr) && awsErr.Code() == "InvalidParameterException" { - response = &cloudwatchlogs.StopQueryOutput{Success: aws.Bool(false)} + if errors.Is(err, &cloudwatchlogstypes.InvalidParameterException{}) { + response = &cloudwatchlogs.StopQueryOutput{Success: false} err = nil } else { err = backend.DownstreamError(err) @@ -341,35 +298,35 @@ func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cl return response, err } -func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery) (*data.Frame, error) { response, err := e.executeStopQuery(ctx, logsClient, logsQuery) if err != nil { return nil, err } - dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{*response.Success})) + dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{response.Success})) return dataFrame, nil } -func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) { queryInput := &cloudwatchlogs.GetQueryResultsInput{ QueryId: aws.String(logsQuery.QueryId), } - getQueryResultsResponse, err := logsClient.GetQueryResultsWithContext(ctx, queryInput) + getQueryResultsResponse, err := logsClient.GetQueryResults(ctx, queryInput) if err != nil { - var awsErr awserr.Error + var awsErr smithy.APIError if errors.As(err, &awsErr) { - err = &AWSError{Code: awsErr.Code(), Message: err.Error()} + err = &AWSError{Code: awsErr.ErrorCode(), Message: awsErr.ErrorMessage()} } err = backend.DownstreamError(err) } return getQueryResultsResponse, err } -func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, logsQuery models.LogsQuery, refID string) (*data.Frame, error) { getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, logsQuery) if err != nil { diff --git a/pkg/tsdb/cloudwatch/log_actions_test.go b/pkg/tsdb/cloudwatch/log_actions_test.go index ae06b447820..0cddf9f10b1 100644 --- a/pkg/tsdb/cloudwatch/log_actions_test.go +++ b/pkg/tsdb/cloudwatch/log_actions_test.go @@ -6,19 +6,15 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" - "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -36,7 +32,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { return &cli } const refID = "A" @@ -57,7 +53,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { EndTime: aws.Int64(1), - Limit: aws.Int64(10), + Limit: aws.Int32(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(false), @@ -76,7 +72,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { StartTime: aws.Int64(1), - Limit: aws.Int64(10), + Limit: aws.Int32(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(true), @@ -88,11 +84,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) - + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -108,8 +100,8 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents }) require.NoError(t, err) - require.Len(t, cli.calls.getEventsWithContext, 1) - assert.Equal(t, test.expectedInput, cli.calls.getEventsWithContext) + require.Len(t, cli.calls.getEvents, 1) + assert.Equal(t, test.expectedInput, cli.calls.getEvents) }) } } @@ -120,17 +112,15 @@ func TestQuery_GetLogEvents_returns_response_from_GetLogEvents_to_data_frame_fie NewCWLogsClient = origNewCWLogsClient }) var cli *mocks.MockLogEvents - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { return cli } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) cli = &mocks.MockLogEvents{} - cli.On("GetLogEventsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ - Events: []*cloudwatchlogs.OutputLogEvent{{ + cli.On("GetLogEvents", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ + Events: []cloudwatchlogstypes.OutputLogEvent{{ Message: utils.Pointer("some message"), Timestamp: utils.Pointer(int64(15)), }}}, nil) @@ -174,7 +164,7 @@ func TestQuery_StartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { return &cli } @@ -183,18 +173,18 @@ func TestQuery_StartQuery(t *testing.T) { cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []*cloudwatchlogs.LogGroupField{ + LogGroupFields: []cloudwatchlogstypes.LogGroupField{ { Name: aws.String("field_a"), - Percent: aws.Int64(100), + Percent: 100, }, { Name: aws.String("field_b"), - Percent: aws.Int64(30), + Percent: 30, }, { Name: aws.String("field_c"), - Percent: aws.Int64(55), + Percent: 55, }, }, }, @@ -205,13 +195,11 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584700643, 0), } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, sessions: &fakeSessionCache{}}, nil - }) + im := testInstanceManagerWithSettings(models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, false) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -241,18 +229,18 @@ func TestQuery_StartQuery(t *testing.T) { const refID = "A" cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []*cloudwatchlogs.LogGroupField{ + LogGroupFields: []cloudwatchlogstypes.LogGroupField{ { Name: aws.String("field_a"), - Percent: aws.Int64(100), + Percent: 100, }, { Name: aws.String("field_b"), - Percent: aws.Int64(30), + Percent: 30, }, { Name: aws.String("field_c"), - Percent: aws.Int64(55), + Percent: 55, }, }, }, @@ -263,13 +251,11 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584873443000, 0), } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, sessions: &fakeSessionCache{}}, nil - }) + im := testInstanceManagerWithSettings(models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, false) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -311,26 +297,6 @@ func TestQuery_StartQuery(t *testing.T) { }) } -type withQueryLanguageMock struct { - capturedLanguage *dataquery.LogsQueryLanguage - mockWithQueryLanguage func(language *dataquery.LogsQueryLanguage) func(request *request.Request) -} - -func newWithQueryLanguageMock() *withQueryLanguageMock { - mock := &withQueryLanguageMock{ - capturedLanguage: new(dataquery.LogsQueryLanguage), - } - - mock.mockWithQueryLanguage = func(language *dataquery.LogsQueryLanguage) func(request *request.Request) { - *mock.capturedLanguage = *language - return func(req *request.Request) { - - } - } - - return mock -} - func Test_executeStartQuery(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { @@ -339,15 +305,15 @@ func Test_executeStartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { return &cli } - t.Run("successfully parses information from JSON to StartQueryWithContext for language", func(t *testing.T) { + t.Run("successfully parses information from JSON to StartQuery for language", func(t *testing.T) { testCases := map[string]struct { queries []backend.DataQuery expectedOutput []*cloudwatchlogs.StartQueryInput - queryLanguage dataquery.LogsQueryLanguage + queryLanguage cloudwatchlogstypes.QueryLanguage }{ "not defined": { queries: []backend.DataQuery{ @@ -366,11 +332,12 @@ func Test_executeStartQuery(t *testing.T) { expectedOutput: []*cloudwatchlogs.StartQueryInput{{ StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, + LogGroupNames: []string{"some name", "another name"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }}, - queryLanguage: dataquery.LogsQueryLanguageCWLI, + queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, "CWLI": { queries: []backend.DataQuery{{ @@ -389,12 +356,13 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, + LogGroupNames: []string{"some name", "another name"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, }, - queryLanguage: dataquery.LogsQueryLanguageCWLI, + queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, "PPL": { queries: []backend.DataQuery{{ @@ -413,12 +381,13 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("source logs | fields @message"), - LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, + LogGroupNames: []string{"some name", "another name"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguagePpl, }, }, - queryLanguage: dataquery.LogsQueryLanguagePPL, + queryLanguage: cloudwatchlogstypes.QueryLanguagePpl, }, "SQL": { queries: []backend.DataQuery{ @@ -439,46 +408,35 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("SELECT * FROM logs"), LogGroupNames: nil, + QueryLanguage: cloudwatchlogstypes.QueryLanguageSql, }, }, - queryLanguage: dataquery.LogsQueryLanguageSQL, + queryLanguage: cloudwatchlogstypes.QueryLanguageSql, }, } for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) - languageMock := newWithQueryLanguageMock() - originalWithQueryLanguage := WithQueryLanguage - WithQueryLanguage = languageMock.mockWithQueryLanguage - defer func() { - WithQueryLanguage = originalWithQueryLanguage - }() - _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, Queries: test.queries, }) assert.NoError(t, err) - assert.Equal(t, test.expectedOutput, cli.calls.startQueryWithContext) - assert.Equal(t, &test.queryLanguage, languageMock.capturedLanguage) + assert.Equal(t, test.expectedOutput, cli.calls.startQuery) }) } }) t.Run("does not populate StartQueryInput.limit when no limit provided", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -496,15 +454,13 @@ func Test_executeStartQuery(t *testing.T) { }) assert.NoError(t, err) - require.Len(t, cli.calls.startQueryWithContext, 1) - assert.Nil(t, cli.calls.startQueryWithContext[0].Limit) + require.Len(t, cli.calls.startQuery, 1) + assert.Nil(t, cli.calls.startQuery[0].Limit) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -530,18 +486,17 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []*string{aws.String("fakeARN")}, + LogGroupIdentifiers: []string{"fakeARN"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, - }, cli.calls.startQueryWithContext) + }, cli.calls.startQuery) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled and strips out trailing *", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -566,18 +521,17 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, + LogGroupIdentifiers: []string{"*fake**ARN"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, - }, cli.calls.startQueryWithContext) + }, cli.calls.startQuery) }) t.Run("uses LogGroupNames if the cross account feature flag is not enabled, and log group names is present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -601,18 +555,17 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []*string{aws.String("/log-group-name")}, + LogGroupNames: []string{"/log-group-name"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, - }, cli.calls.startQueryWithContext) + }, cli.calls.startQuery) }) t.Run("ignores logGroups if feature flag is disabled even if logGroupNames is not present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -635,18 +588,17 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []*string{}, + LogGroupNames: nil, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, - }, cli.calls.startQueryWithContext) + }, cli.calls.startQuery) }) t.Run("it always uses logGroups when feature flag is enabled and ignores log group names", func(t *testing.T) { cli = fakeCWLogsClient{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -670,11 +622,12 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int64(12), + Limit: aws.Int32(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, + LogGroupIdentifiers: []string{"*fake**ARN"}, + QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, }, - }, cli.calls.startQueryWithContext) + }, cli.calls.startQuery) }) } @@ -686,32 +639,30 @@ func TestQuery_StopQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(aws.Config) models.CWLogsClient { return &cli } cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []*cloudwatchlogs.LogGroupField{ + LogGroupFields: []cloudwatchlogstypes.LogGroupField{ { Name: aws.String("field_a"), - Percent: aws.Int64(100), + Percent: 100, }, { Name: aws.String("field_b"), - Percent: aws.Int64(30), + Percent: 30, }, { Name: aws.String("field_c"), - Percent: aws.Int64(55), + Percent: 55, }, }, }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() timeRange := backend.TimeRange{ From: time.Unix(1584873443, 0), @@ -758,14 +709,14 @@ func TestQuery_GetQueryResults(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(aws.Config) models.CWLogsClient { return &cli } const refID = "A" cli = fakeCWLogsClient{ queryResults: cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]*cloudwatchlogs.ResultField{ + Results: [][]cloudwatchlogstypes.ResultField{ { { Field: aws.String("@timestamp"), @@ -795,18 +746,16 @@ func TestQuery_GetQueryResults(t *testing.T) { }, }, }, - Statistics: &cloudwatchlogs.QueryStatistics{ - BytesScanned: aws.Float64(512), - RecordsMatched: aws.Float64(256), - RecordsScanned: aws.Float64(1024), + Statistics: &cloudwatchlogstypes.QueryStatistics{ + BytesScanned: 512, + RecordsMatched: 256, + RecordsScanned: 1024, }, - Status: aws.String("Complete"), + Status: "Complete", }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/log_query.go b/pkg/tsdb/cloudwatch/log_query.go index eab124843f6..e4f8b0e4dc3 100644 --- a/pkg/tsdb/cloudwatch/log_query.go +++ b/pkg/tsdb/cloudwatch/log_query.go @@ -7,7 +7,9 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana-plugin-sdk-go/data" ) @@ -18,7 +20,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return nil, fmt.Errorf("response is nil, cannot convert log results to data frames") } - nonEmptyRows := make([][]*cloudwatchlogs.ResultField, 0) + nonEmptyRows := make([][]cloudwatchlogstypes.ResultField, 0) for _, row := range response.Results { // Sometimes CloudWatch can send empty rows if len(row) == 0 { @@ -115,26 +117,20 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro queryStats := make([]data.QueryStat, 0) if response.Statistics != nil { - if response.Statistics.BytesScanned != nil { - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, - Value: *response.Statistics.BytesScanned, - }) - } - - if response.Statistics.RecordsScanned != nil { - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, - Value: *response.Statistics.RecordsScanned, - }) - } - - if response.Statistics.RecordsMatched != nil { - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, - Value: *response.Statistics.RecordsMatched, - }) - } + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, + Value: response.Statistics.BytesScanned, + }) + + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, + Value: response.Statistics.RecordsScanned, + }) + + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, + Value: response.Statistics.RecordsMatched, + }) } frame := data.NewFrame("CloudWatchLogsResponse", newFields...) @@ -147,10 +143,8 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro frame.Meta.Stats = queryStats } - if response.Status != nil { - frame.Meta.Custom = map[string]any{ - "Status": *response.Status, - } + frame.Meta.Custom = map[string]any{ + "Status": string(response.Status), } // Results aren't guaranteed to come ordered by time (ascending), so we need to sort @@ -158,7 +152,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return frame, nil } -func changeToStringField(lengthOfValues int, rows [][]*cloudwatchlogs.ResultField, logEventField string) []*string { +func changeToStringField(lengthOfValues int, rows [][]cloudwatchlogstypes.ResultField, logEventField string) []*string { fieldValuesAsStrings := make([]*string, lengthOfValues) for i, resultFields := range rows { for _, field := range resultFields { diff --git a/pkg/tsdb/cloudwatch/log_query_test.go b/pkg/tsdb/cloudwatch/log_query_test.go index a112451915e..9e86b4e752c 100644 --- a/pkg/tsdb/cloudwatch/log_query_test.go +++ b/pkg/tsdb/cloudwatch/log_query_test.go @@ -5,8 +5,10 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" @@ -19,63 +21,63 @@ import ( func TestLogsResultsToDataframes(t *testing.T) { fakeCloudwatchResponse := &cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]*cloudwatchlogs.ResultField{ + Results: [][]cloudwatchlogstypes.ResultField{ { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 15:04:05.000"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("line"), Value: aws.String("test message 1"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 16:04:05.000"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("line"), Value: aws.String("test message 2"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, @@ -84,47 +86,47 @@ func TestLogsResultsToDataframes(t *testing.T) { {}, // or rows with only timestamp { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, }, { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("line"), Value: aws.String("test message 3"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, }, - Status: aws.String("ok"), - Statistics: &cloudwatchlogs.QueryStatistics{ - BytesScanned: aws.Float64(2000), - RecordsMatched: aws.Float64(3), - RecordsScanned: aws.Float64(5000), + Status: "ok", + Statistics: &cloudwatchlogstypes.QueryStatistics{ + BytesScanned: 2000, + RecordsMatched: 3, + RecordsScanned: 5000, }, } @@ -224,33 +226,33 @@ func TestLogsResultsToDataframes(t *testing.T) { func TestLogsResultsToDataframes_MixedTypes_NumericValuesMixedWithStringFallBackToStringValues(t *testing.T) { dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]*cloudwatchlogs.ResultField{ + Results: [][]cloudwatchlogstypes.ResultField{ { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("-1.234"), }, }, { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("1"), }, }, { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("not a number"), }, }, { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("2.000"), }, }, }, - Status: aws.String("ok"), + Status: "ok", }, []string{}) require.NoError(t, err) @@ -284,27 +286,27 @@ func TestLogsResultsToDataframes_With_Millisecond_Timestamps(t *testing.T) { ingestionTimeField := int64(1732790372916) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]*cloudwatchlogs.ResultField{ + Results: [][]cloudwatchlogstypes.ResultField{ { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@ingestionTime"), Value: aws.String(fmt.Sprintf("%d", ingestionTimeField)), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("stringTimeField"), Value: aws.String(stringTimeField), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("message"), Value: aws.String("log message"), }, }, }, - Status: aws.String("ok"), + Status: "ok", }, []string{}) require.NoError(t, err) @@ -348,23 +350,23 @@ func TestLogsResultsToDataframes_With_Int_Grouping_Field(t *testing.T) { timestampField := int64(1732749534876) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]*cloudwatchlogs.ResultField{ + Results: [][]cloudwatchlogstypes.ResultField{ { - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("numberField"), Value: aws.String("8"), }, - &cloudwatchlogs.ResultField{ + cloudwatchlogstypes.ResultField{ Field: aws.String("groupingNumber"), Value: aws.String("100"), }, }, }, - Status: aws.String("ok"), + Status: "ok", }, []string{"groupingNumber"}) require.NoError(t, err) diff --git a/pkg/tsdb/cloudwatch/log_sync_query.go b/pkg/tsdb/cloudwatch/log_sync_query.go index fb3f099a9b4..a2ee420bf4e 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query.go +++ b/pkg/tsdb/cloudwatch/log_sync_query.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -86,7 +86,7 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req * return resp, nil } -func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, +func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient models.CWLogsClient, queryContext backend.DataQuery, logsQuery models.LogsQuery, logsTimeout time.Duration) (*cloudwatchlogs.GetQueryResultsOutput, error) { startQueryOutput, err := e.executeStartQuery(ctx, logsClient, logsQuery, queryContext.TimeRange) if err != nil { @@ -117,7 +117,7 @@ func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatc if err != nil { return nil, err } - if isTerminated(*res.Status) { + if isTerminated(res.Status) { return res, err } if time.Duration(attemptCount)*time.Second >= logsTimeout { diff --git a/pkg/tsdb/cloudwatch/log_sync_query_test.go b/pkg/tsdb/cloudwatch/log_sync_query_test.go index cb1f8d0cab3..eeb228dac8b 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query_test.go +++ b/pkg/tsdb/cloudwatch/log_sync_query_test.go @@ -7,14 +7,12 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" - "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -31,16 +29,13 @@ func Test_executeSyncLogQuery(t *testing.T) { }) var cli fakeCWLogsClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(aws.Config) models.CWLogsClient { return &cli } t.Run("getCWLogsClient is called with region from input JSON", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} - sess := fakeSessionCache{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &sess}, nil - }) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -58,15 +53,12 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - assert.Equal(t, []string{"some region"}, sess.calledRegions) + //assert.Equal(t, []string{"some region"}, sess.calledRegions) }) t.Run("getCWLogsClient is called with region from instance manager when region is default", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} - sess := fakeSessionCache{} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &sess}, nil - }) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} + im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -84,7 +76,7 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) + //assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) }) t.Run("with header", func(t *testing.T) { @@ -119,10 +111,8 @@ func Test_executeSyncLogQuery(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { syncCalled = false - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil - }) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} + im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -161,10 +151,8 @@ func Test_executeSyncLogQuery(t *testing.T) { executeSyncLogQuery = origExecuteSyncLogQuery }) - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil - }) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} + im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -192,19 +180,17 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }) var cli *mockLogsSyncClient - NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + NewCWLogsClient = func(aws.Config) models.CWLogsClient { return cli } t.Run("when a query refId is not provided, 'A' is assigned by default", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -227,13 +213,11 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { t.Run("when a query refId is provided, it is returned in the response", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -269,40 +253,38 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { // when each query has a different response from AWS API calls, the RefIds are correctly reassigned to the associated response. cli = &mockLogsSyncClient{} // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for A" - cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for A" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for A"), }, nil) // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for B" - cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for B" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for B"), }, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for A" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for A" - Results: [][]*cloudwatchlogs.ResultField{{{ + Results: [][]cloudwatchlogstypes.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("A result"), }}}, - Status: aws.String("Complete")}, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + Status: "Complete"}, nil) + cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for B" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for B" - Results: [][]*cloudwatchlogs.ResultField{{{ + Results: [][]cloudwatchlogstypes.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("B result"), }}}, - Status: aws.String("Complete")}, nil) + Status: "Complete"}, nil) - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -328,26 +310,24 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) - expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId A + expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResults matches the input RefId A assert.NoError(t, err) respA, ok := res.Responses["A"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromFirstCall}, respA.Frames[0].Fields) - expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId B + expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResults matches the input RefId B respB, ok := res.Responses["B"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromSecondCall}, respB.Frames[0].Fields) }) t.Run("when logsTimeout setting is defined, the polling period will be set to that variable", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Running")}, nil) - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, sessions: &fakeSessionCache{}}, nil - }) + cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Running"}, nil) + im := testInstanceManagerWithSettings(models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, false) executor := newExecutor(im, log.NewNullLogger()) @@ -366,21 +346,19 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) assert.Error(t, err) - cli.AssertNumberOfCalls(t, "GetQueryResultsWithContext", 1) + cli.AssertNumberOfCalls(t, "GetQueryResults", 1) }) t.Run("when getQueryResults returns aws error is returned, it keeps the context", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return( - &cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, - &fakeAWSError{code: "foo", message: "bar"}, + cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return( + &cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, + &fakeSmithyError{code: "foo", message: "bar"}, ) - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder.go b/pkg/tsdb/cloudwatch/metric_data_input_builder.go index 2bbf4da5df4..b659127c658 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder.go @@ -4,8 +4,9 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) @@ -15,13 +16,13 @@ func (e *cloudWatchExecutor) buildMetricDataInput(ctx context.Context, startTime metricDataInput := &cloudwatch.GetMetricDataInput{ StartTime: aws.Time(startTime), EndTime: aws.Time(endTime), - ScanBy: aws.String("TimestampAscending"), + ScanBy: cloudwatchtypes.ScanByTimestampAscending, } shouldSetLabelOptions := len(queries) > 0 && len(queries[0].TimezoneUTCOffset) > 0 if shouldSetLabelOptions { - metricDataInput.LabelOptions = &cloudwatch.LabelOptions{ + metricDataInput.LabelOptions = &cloudwatchtypes.LabelOptions{ Timezone: aws.String(queries[0].TimezoneUTCOffset), } } diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go index 5b8ba2beb53..efb7e1d5a82 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go @@ -5,8 +5,9 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -20,9 +21,9 @@ func TestMetricDataInputBuilder(t *testing.T) { tests := []struct { name string timezoneUTCOffset string - expectedLabelOptions *cloudwatch.LabelOptions + expectedLabelOptions *cloudwatchtypes.LabelOptions }{ - {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatch.LabelOptions{Timezone: aws.String("+1234")}}, + {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatchtypes.LabelOptions{Timezone: aws.String("+1234")}}, {name: "when timezoneUTCOffset is not provided", timezoneUTCOffset: "", expectedLabelOptions: nil}, } diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder.go b/pkg/tsdb/cloudwatch/metric_data_query_builder.go index fbed54f5950..2edb4f35be8 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder.go @@ -4,11 +4,10 @@ import ( "context" "fmt" "sort" - "strconv" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -16,8 +15,8 @@ import ( const keySeparator = "|&|" -func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) { - mdq := &cloudwatch.MetricDataQuery{ +func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (cloudwatchtypes.MetricDataQuery, error) { + mdq := cloudwatchtypes.MetricDataQuery{ Id: aws.String(query.Id), ReturnData: aws.Bool(query.ReturnData), } @@ -28,10 +27,10 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo switch query.GetGetMetricDataAPIMode() { case models.GMDApiModeMathExpression: - mdq.Period = aws.Int64(int64(query.Period)) + mdq.Period = aws.Int32(int32(query.Period)) mdq.Expression = aws.String(query.Expression) case models.GMDApiModeSQLExpression: - mdq.Period = aws.Int64(int64(query.Period)) + mdq.Period = aws.Int32(int32(query.Period)) mdq.Expression = aws.String(query.SqlExpression) case models.GMDApiModeInferredSearchExpression: mdq.Expression = aws.String(buildSearchExpression(query, query.Statistic)) @@ -39,17 +38,17 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo mdq.Label = aws.String(buildSearchExpressionLabel(query)) } case models.GMDApiModeMetricStat: - mdq.MetricStat = &cloudwatch.MetricStat{ - Metric: &cloudwatch.Metric{ + mdq.MetricStat = &cloudwatchtypes.MetricStat{ + Metric: &cloudwatchtypes.Metric{ Namespace: aws.String(query.Namespace), MetricName: aws.String(query.MetricName), - Dimensions: make([]*cloudwatch.Dimension, 0), + Dimensions: make([]cloudwatchtypes.Dimension, 0), }, - Period: aws.Int64(int64(query.Period)), + Period: aws.Int32(int32(query.Period)), } for key, values := range query.Dimensions { mdq.MetricStat.Metric.Dimensions = append(mdq.MetricStat.Metric.Dimensions, - &cloudwatch.Dimension{ + cloudwatchtypes.Dimension{ Name: aws.String(key), Value: aws.String(values[0]), }) @@ -121,14 +120,14 @@ func buildSearchExpression(query *models.CloudWatchQuery, stat string) string { } schema = fmt.Sprintf("{%s}", schema) schemaSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{schema, searchTerm, account}, " ")) - return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %s))", schemaSearchTermAndAccount, stat, strconv.Itoa(query.Period)) + return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %d))", schemaSearchTermAndAccount, stat, query.Period) } sort.Strings(dimensionNamesWithoutKnownValues) searchTerm = appendSearch(searchTerm, join(dimensionNamesWithoutKnownValues, " ", `"`, `"`)) namespace := fmt.Sprintf("Namespace=%q", query.Namespace) namespaceSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{namespace, searchTerm, account}, " ")) - return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %s))`, namespaceSearchTermAndAccount, stat, strconv.Itoa(query.Period)) + return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %d))`, namespaceSearchTermAndAccount, stat, query.Period) } func buildSearchExpressionLabel(query *models.CloudWatchQuery) string { diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go index 907516f0d80..e6e74b8acf5 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go @@ -4,7 +4,8 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -88,7 +89,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) - assert.Equal(t, int64(300), *mdq.Period) + assert.Equal(t, int32(300), *mdq.Period) assert.Equal(t, `SUM([a,b])`, *mdq.Expression) }) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 77b41f1669a..ce618787859 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -12,10 +12,14 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" + "github.com/grafana/grafana-plugin-sdk-go/backend" ) @@ -43,7 +47,7 @@ func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCt region := parameters.Get("region") instanceId := parameters.Get("instanceId") - instanceIds := aws.StringSlice(parseMultiSelectValue(instanceId)) + instanceIds := parseMultiSelectValue(instanceId) instances, err := e.ec2DescribeInstances(ctx, pluginCtx, region, nil, instanceIds) if err != nil { return nil, err @@ -72,16 +76,16 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []*ec2.Filter + var filters []ec2types.Filter for k, v := range filterMap { if vv, ok := v.([]any); ok { - var values []*string + var values []string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, &vvvv) + values = append(values, vvvv) } } - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(k), Values: values, }) @@ -120,7 +124,7 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return result, nil } -func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (value string, found bool, err error) { +func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) (value string, found bool, err error) { tags := make(map[string]string) for _, tag := range instance.Tags { tags[*tag.Key] = *tag.Value @@ -152,7 +156,12 @@ func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (va if v.Kind() == reflect.Ptr && v.IsNil() { return "", false, nil } - if attr, ok := v.Interface().(*string); ok { + if v.Kind() == reflect.String { + if v.String() == "" { + return "", false, nil + } + data = v.String() + } else if attr, ok := v.Interface().(*string); ok { data = *attr } else if attr, ok := v.Interface().(*time.Time); ok { data = attr.String() @@ -179,24 +188,23 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []*resourcegroupstaggingapi.TagFilter + var filters []resourcegroupstaggingapitypes.TagFilter for k, v := range tagsMap { if vv, ok := v.([]any); ok { - var values []*string + var values []string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, &vvvv) + values = append(values, vvvv) } } - filters = append(filters, &resourcegroupstaggingapi.TagFilter{ + filters = append(filters, resourcegroupstaggingapitypes.TagFilter{ Key: aws.String(k), Values: values, }) } } - var resourceTypes []*string - resourceTypes = append(resourceTypes, &resourceType) + resourceTypes := []string{resourceType} resources, err := e.resourceGroupsGetResources(ctx, pluginCtx, region, filters, resourceTypes) if err != nil { @@ -212,7 +220,7 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return result, nil } -func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) { +func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []ec2types.Filter, instanceIds []string) (*ec2.DescribeInstancesOutput, error) { params := &ec2.DescribeInstancesInput{ Filters: filters, InstanceIds: instanceIds, @@ -223,19 +231,20 @@ func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx return nil, err } - var resp ec2.DescribeInstancesOutput - if err := client.DescribeInstancesPagesWithContext(ctx, params, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { + resp := &ec2.DescribeInstancesOutput{} + pager := ec2.NewDescribeInstancesPaginator(client, params) + for pager.HasMorePages() { + page, err := pager.NextPage(ctx) + if err != nil { + return resp, fmt.Errorf("describe instances pager failed: %w", err) + } resp.Reservations = append(resp.Reservations, page.Reservations...) - return !lastPage - }); err != nil { - return nil, fmt.Errorf("failed to call ec2:DescribeInstances, %w", err) } - - return &resp, nil + return resp, nil } -func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*resourcegroupstaggingapi.TagFilter, - resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { +func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []resourcegroupstaggingapitypes.TagFilter, + resourceTypes []string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { params := &resourcegroupstaggingapi.GetResourcesInput{ ResourceTypeFilters: resourceTypes, TagFilters: filters, @@ -247,12 +256,13 @@ func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, plu } var resp resourcegroupstaggingapi.GetResourcesOutput - if err := client.GetResourcesPagesWithContext(ctx, params, - func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool { - resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) - return !lastPage - }); err != nil { - return nil, fmt.Errorf("failed to call tag:GetResources, %w", err) + paginator := resourcegroupstaggingapi.NewGetResourcesPaginator(client, params) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("get resource groups paginator failed: %w", err) + } + resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) } return &resp, nil @@ -270,17 +280,17 @@ func (e *cloudWatchExecutor) handleGetLogGroups(ctx context.Context, pluginCtx b } logGroupLimit := defaultLogGroupLimit - intLimit, err := strconv.ParseInt(limit, 10, 64) + intLimit, err := strconv.ParseInt(limit, 10, 32) if err == nil && intLimit > 0 { - logGroupLimit = intLimit + logGroupLimit = int32(intLimit) } - input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(logGroupLimit)} + input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(logGroupLimit)} if len(logGroupNamePrefix) > 0 { input.LogGroupNamePrefix = aws.String(logGroupNamePrefix) } var response *cloudwatchlogs.DescribeLogGroupsOutput - response, err = logsClient.DescribeLogGroupsWithContext(ctx, input) + response, err = logsClient.DescribeLogGroups(ctx, input) if err != nil || response == nil { return nil, err } diff --git a/pkg/tsdb/cloudwatch/metric_find_query_test.go b/pkg/tsdb/cloudwatch/metric_find_query_test.go index 85329bd5760..7a4598896ab 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query_test.go +++ b/pkg/tsdb/cloudwatch/metric_find_query_test.go @@ -6,14 +6,12 @@ import ( "net/url" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" + "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" - "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -21,26 +19,26 @@ import ( ) func TestQuery_InstanceAttributes(t *testing.T) { - origNewEC2Client := NewEC2Client + origNewEC2API := NewEC2API t.Cleanup(func() { - NewEC2Client = origNewEC2Client + NewEC2API = origNewEC2API }) var cli oldEC2Client - NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { + NewEC2API = func(aws.Config) models.EC2APIProvider { return cli } t.Run("Get instance ID", func(t *testing.T) { const instanceID = "i-12345678" cli = oldEC2Client{ - reservations: []*ec2.Reservation{ + reservations: []ec2types.Reservation{ { - Instances: []*ec2.Instance{ + Instances: []ec2types.Instance{ { InstanceId: aws.String(instanceID), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -52,9 +50,7 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() filterMap := map[string][]string{ "tag:Environment": {"production"}, @@ -82,17 +78,17 @@ func TestQuery_InstanceAttributes(t *testing.T) { }) t.Run("Get different types", func(t *testing.T) { - var expectedInt int64 = 3 + var expectedInt int32 = 3 var expectedBool = true var expectedArn = "arn" cli = oldEC2Client{ - reservations: []*ec2.Reservation{ + reservations: []ec2types.Reservation{ { - Instances: []*ec2.Instance{ + Instances: []ec2types.Instance{ { AmiLaunchIndex: &expectedInt, EbsOptimized: &expectedBool, - IamInstanceProfile: &ec2.IamInstanceProfile{ + IamInstanceProfile: &ec2types.IamInstanceProfile{ Arn: &expectedArn, }, }, @@ -101,9 +97,7 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) @@ -163,52 +157,52 @@ func TestQuery_InstanceAttributes(t *testing.T) { } func TestQuery_EBSVolumeIDs(t *testing.T) { - origNewEC2Client := NewEC2Client + origNewEC2API := NewEC2API t.Cleanup(func() { - NewEC2Client = origNewEC2Client + NewEC2API = origNewEC2API }) var cli oldEC2Client - NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { + NewEC2API = func(aws.Config) models.EC2APIProvider { return cli } t.Run("", func(t *testing.T) { cli = oldEC2Client{ - reservations: []*ec2.Reservation{ + reservations: []ec2types.Reservation{ { - Instances: []*ec2.Instance{ + Instances: []ec2types.Instance{ { InstanceId: aws.String("i-1"), - BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, + BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, }, }, { InstanceId: aws.String("i-2"), - BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, + BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, }, }, }, }, { - Instances: []*ec2.Instance{ + Instances: []ec2types.Instance{ { InstanceId: aws.String("i-3"), - BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, + BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, }, }, { InstanceId: aws.String("i-4"), - BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, - {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, + BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, + {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, }, }, }, @@ -216,9 +210,7 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.handleGetEbsVolumeIds( @@ -242,23 +234,23 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { } func TestQuery_ResourceARNs(t *testing.T) { - origNewRGTAClient := newRGTAClient + origNewRGTAClient := NewRGTAClient t.Cleanup(func() { - newRGTAClient = origNewRGTAClient + NewRGTAClient = origNewRGTAClient }) var cli fakeRGTAClient - newRGTAClient = func(client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { + NewRGTAClient = func(aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { return cli } t.Run("", func(t *testing.T) { cli = fakeRGTAClient{ - tagMapping: []*resourcegroupstaggingapi.ResourceTagMapping{ + tagMapping: []resourcegroupstaggingapitypes.ResourceTagMapping{ { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567"), - Tags: []*resourcegroupstaggingapi.Tag{ + Tags: []resourcegroupstaggingapitypes.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -267,7 +259,7 @@ func TestQuery_ResourceARNs(t *testing.T) { }, { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321"), - Tags: []*resourcegroupstaggingapi.Tag{ + Tags: []resourcegroupstaggingapitypes.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -277,9 +269,7 @@ func TestQuery_ResourceARNs(t *testing.T) { }, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() tagMap := map[string][]string{ "Environment": {"production"}, diff --git a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go index cdb1aeca82f..86040e165a2 100644 --- a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go +++ b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go @@ -1,71 +1,65 @@ package mocks import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" + "github.com/stretchr/testify/mock" ) type FakeMetricsAPI struct { - Metrics []*cloudwatch.Metric - OwningAccounts []*string + models.CWClient + + Metrics []cloudwatchtypes.Metric + OwningAccounts []string MetricsPerPage int + + cursor int } -func (c *FakeMetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { +func (c *FakeMetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { if c.MetricsPerPage == 0 { c.MetricsPerPage = 1000 } - chunks := chunkSlice(c.Metrics, c.MetricsPerPage) - - for i, metrics := range chunks { - response := fn(&cloudwatch.ListMetricsOutput{ - Metrics: metrics, - OwningAccounts: c.OwningAccounts, - }, i+1 == len(chunks)) - if !response { - break + var metrics []cloudwatchtypes.Metric + nextToken := aws.String("yes") + if c.cursor < len(c.Metrics) { + end := c.cursor + c.MetricsPerPage + if end > len(c.Metrics) { + end = len(c.Metrics) + nextToken = nil } + metrics = c.Metrics[c.cursor:end] } - return nil -} + c.cursor += c.MetricsPerPage -func chunkSlice(slice []*cloudwatch.Metric, chunkSize int) [][]*cloudwatch.Metric { - var chunks [][]*cloudwatch.Metric - for { - if len(slice) == 0 { - break - } - if len(slice) < chunkSize { - chunkSize = len(slice) - } - - chunks = append(chunks, slice[0:chunkSize]) - slice = slice[chunkSize:] - } - - return chunks + return &cloudwatch.ListMetricsOutput{ + Metrics: metrics, + OwningAccounts: c.OwningAccounts, + NextToken: nextToken, + }, nil } type MetricsAPI struct { - cloudwatchiface.CloudWatchAPI mock.Mock + models.CWClient - Metrics []*cloudwatch.Metric + Metrics []cloudwatchtypes.Metric } -func (m *MetricsAPI) GetMetricDataWithContext(ctx aws.Context, input *cloudwatch.GetMetricDataInput, opts ...request.Option) (*cloudwatch.GetMetricDataOutput, error) { - args := m.Called(ctx, input, opts) +func (m *MetricsAPI) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { + args := m.Called(ctx, input, optFns) return args.Get(0).(*cloudwatch.GetMetricDataOutput), args.Error(1) } -func (m *MetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { - fn(&cloudwatch.ListMetricsOutput{ +func (m *MetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { + return &cloudwatch.ListMetricsOutput{ Metrics: m.Metrics, - }, true) - - return m.Called().Error(0) + }, m.Called().Error(0) } diff --git a/pkg/tsdb/cloudwatch/mocks/logs.go b/pkg/tsdb/cloudwatch/mocks/logs.go index e0b16bf436d..7843fdaacd1 100644 --- a/pkg/tsdb/cloudwatch/mocks/logs.go +++ b/pkg/tsdb/cloudwatch/mocks/logs.go @@ -3,10 +3,7 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -16,13 +13,13 @@ type LogsAPI struct { mock.Mock } -func (l *LogsAPI) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (l *LogsAPI) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.DescribeLogGroupsOutput), args.Error(1) } -func (l *LogsAPI) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (l *LogsAPI) GetLogGroupFields(_ context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.GetLogGroupFieldsOutput), args.Error(1) @@ -32,26 +29,40 @@ type LogsService struct { mock.Mock } -func (l *LogsService) GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (l *LogsService) GetLogGroups(_ context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroup]), args.Error(1) } -func (l *LogsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (l *LogsService) GetLogGroupFields(_ context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroupField]), args.Error(1) } type MockLogEvents struct { - cloudwatchlogsiface.CloudWatchLogsAPI - mock.Mock } -func (m *MockLogEvents) GetLogEventsWithContext(ctx aws.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { - args := m.Called(ctx, input, option) +func (m *MockLogEvents) StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { + return nil, nil +} + +func (m *MockLogEvents) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { + return nil, nil +} + +func (m *MockLogEvents) GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { + return nil, nil +} + +func (m *MockLogEvents) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + return nil, nil +} + +func (m *MockLogEvents) GetLogEvents(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { + args := m.Called(ctx, input, optFns) return args.Get(0).(*cloudwatchlogs.GetLogEventsOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/metrics_client.go b/pkg/tsdb/cloudwatch/mocks/metrics_client.go index 44ab1fbcb5b..adf23796945 100644 --- a/pkg/tsdb/cloudwatch/mocks/metrics_client.go +++ b/pkg/tsdb/cloudwatch/mocks/metrics_client.go @@ -3,7 +3,8 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -12,7 +13,7 @@ type FakeMetricsClient struct { mock.Mock } -func (m *FakeMetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { +func (m *FakeMetricsClient) ListMetricsWithPageLimit(_ context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { args := m.Called(params) return args.Get(0).([]resources.MetricResponse), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/oam_client.go b/pkg/tsdb/cloudwatch/mocks/oam_client.go index 932ca1579b4..d16253e3252 100644 --- a/pkg/tsdb/cloudwatch/mocks/oam_client.go +++ b/pkg/tsdb/cloudwatch/mocks/oam_client.go @@ -3,8 +3,7 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/oam" + "github.com/aws/aws-sdk-go-v2/service/oam" "github.com/stretchr/testify/mock" ) @@ -12,12 +11,12 @@ type FakeOAMClient struct { mock.Mock } -func (o *FakeOAMClient) ListSinksWithContext(ctx context.Context, input *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) { +func (o *FakeOAMClient) ListSinks(_ context.Context, input *oam.ListSinksInput, _ ...func(*oam.Options)) (*oam.ListSinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListSinksOutput), args.Error(1) } -func (o *FakeOAMClient) ListAttachedLinksWithContext(ctx context.Context, input *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) { +func (o *FakeOAMClient) ListAttachedLinks(_ context.Context, input *oam.ListAttachedLinksInput, _ ...func(*oam.Options)) (*oam.ListAttachedLinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListAttachedLinksOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/regions.go b/pkg/tsdb/cloudwatch/mocks/regions.go index 1d0ff115ea7..a7717481516 100644 --- a/pkg/tsdb/cloudwatch/mocks/regions.go +++ b/pkg/tsdb/cloudwatch/mocks/regions.go @@ -3,9 +3,7 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -14,7 +12,7 @@ type RegionsService struct { mock.Mock } -func (r *RegionsService) GetRegions(ctx context.Context) (in []resources.ResourceResponse[resources.Region], e error) { +func (r *RegionsService) GetRegions(_ context.Context) (in []resources.ResourceResponse[resources.Region], e error) { args := r.Called() return args.Get(0).(([]resources.ResourceResponse[resources.Region])), args.Error(1) } @@ -23,12 +21,12 @@ type EC2Mock struct { mock.Mock } -func (e *EC2Mock) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) { +func (e *EC2Mock) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { args := e.Called() return args.Get(0).(*ec2.DescribeRegionsOutput), args.Error(1) } -func (e *EC2Mock) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { - args := e.Called(in, fn) - return args.Error(0) +func (e *EC2Mock) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { + args := e.Called(in) + return nil, args.Error(0) } diff --git a/pkg/tsdb/cloudwatch/models/api.go b/pkg/tsdb/cloudwatch/models/api.go index c54c156b123..35d60b7febc 100644 --- a/pkg/tsdb/cloudwatch/models/api.go +++ b/pkg/tsdb/cloudwatch/models/api.go @@ -4,11 +4,11 @@ import ( "context" "net/url" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/oam" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/oam" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -19,12 +19,13 @@ type RequestContextFactoryFunc func(ctx context.Context, pluginCtx backend.Plugi type RouteHandlerFunc func(ctx context.Context, pluginCtx backend.PluginContext, reqContextFactory RequestContextFactoryFunc, parameters url.Values) ([]byte, *HttpError) type RequestContext struct { - MetricsClientProvider MetricsClientProvider - LogsAPIProvider CloudWatchLogsAPIProvider - OAMAPIProvider OAMAPIProvider - EC2APIProvider EC2APIProvider - Settings CloudWatchSettings - Logger log.Logger + MetricsClientProvider MetricsClientProvider + ListMetricsAPIProvider cloudwatch.ListMetricsAPIClient + LogsAPIProvider CloudWatchLogsAPIProvider + OAMAPIProvider OAMAPIProvider + EC2APIProvider EC2APIProvider + Settings CloudWatchSettings + Logger log.Logger } // Services @@ -35,8 +36,8 @@ type ListMetricsProvider interface { } type LogGroupsProvider interface { - GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) - GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) + GetLogGroups(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) + GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) } type AccountsProvider interface { @@ -54,20 +55,42 @@ type MetricsClientProvider interface { // APIs - instead of using the API defined in the services within the aws-sdk-go directly, specify a subset of the API with methods that are actually used in a service or a client type CloudWatchMetricsAPIProvider interface { - ListMetricsPagesWithContext(ctx context.Context, in *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error + ListMetrics(ctx context.Context, in *cloudwatch.ListMetricsInput, optFns ...func(*cloudwatch.Options)) error } type CloudWatchLogsAPIProvider interface { - DescribeLogGroupsWithContext(ctx context.Context, in *cloudwatchlogs.DescribeLogGroupsInput, opts ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) - GetLogGroupFieldsWithContext(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) + cloudwatchlogs.DescribeLogGroupsAPIClient + GetLogGroupFields(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) } type OAMAPIProvider interface { - ListSinksWithContext(ctx context.Context, in *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) - ListAttachedLinksWithContext(ctx context.Context, in *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) + ListSinks(ctx context.Context, in *oam.ListSinksInput, optFns ...func(options *oam.Options)) (*oam.ListSinksOutput, error) + ListAttachedLinks(ctx context.Context, in *oam.ListAttachedLinksInput, optFns ...func(options *oam.Options)) (*oam.ListAttachedLinksOutput, error) } type EC2APIProvider interface { - DescribeRegionsWithContext(ctx context.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) - DescribeInstancesPagesWithContext(ctx context.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error + DescribeRegions(ctx context.Context, in *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) + ec2.DescribeInstancesAPIClient +} + +type CWLogsClient interface { + StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) + StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) + GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) + + cloudwatchlogs.GetLogEventsAPIClient + cloudwatchlogs.DescribeLogGroupsAPIClient +} + +type CWClient interface { + AlarmsAPI + cloudwatch.GetMetricDataAPIClient + cloudwatch.ListMetricsAPIClient +} + +type AlarmsAPI interface { + cloudwatch.DescribeAlarmsAPIClient + cloudwatch.DescribeAlarmHistoryAPIClient + + DescribeAlarmsForMetric(context.Context, *cloudwatch.DescribeAlarmsForMetricInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go index 6907257c55e..301b2d59569 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go @@ -11,7 +11,8 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/google/uuid" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -201,7 +202,11 @@ func (q *CloudWatchQuery) BuildDeepLink(startTime time.Time, endTime time.Time) return "", fmt.Errorf("could not marshal link: %w", err) } - url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, getEndpoint(q.Region))) + endpoint, err := getEndpoint(q.Region) + if err != nil { + return "", err + } + url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, endpoint)) if err != nil { return "", fmt.Errorf("unable to parse CloudWatch console deep link") } @@ -503,14 +508,18 @@ func parseDimensions(dimensions dataquery.Dimensions) (map[string][]string, erro return parsedDimensions, nil } -func getEndpoint(region string) string { - partition, _ := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region) - url := defaultConsoleURL - if partition.ID() == endpoints.AwsUsGovPartitionID { - url = usGovConsoleURL +func getEndpoint(region string) (string, error) { + resolver := cloudwatch.NewDefaultEndpointResolver() + endpoint, err := resolver.ResolveEndpoint(region, cloudwatch.EndpointResolverOptions{}) + if err != nil { + return "", fmt.Errorf("resolve endpoint failed: %w", err) } - if partition.ID() == endpoints.AwsCnPartitionID { - url = chinaConsoleURL + consoleURL := defaultConsoleURL + switch endpoint.PartitionID { + case "aws-us-gov": + consoleURL = usGovConsoleURL + case "aws-cn": + consoleURL = chinaConsoleURL } - return fmt.Sprintf("%s.%s", region, url) + return fmt.Sprintf("%s.%s", region, consoleURL), nil } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go index fe438fb2afd..d38fdd9c659 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go @@ -7,13 +7,15 @@ import ( "testing" "time" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) var logger = log.NewNullLogger() @@ -932,7 +934,6 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create for name, tc := range testCases { t.Run(name, func(t *testing.T) { average := "Average" - false := false queryToMigrate := metricsDataQuery{ CloudWatchMetricsQuery: dataquery.CloudWatchMetricsQuery{ @@ -945,7 +946,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create }, Statistic: &average, Period: utils.Pointer("600"), - Hide: &false, + Hide: aws.Bool(false), }, } @@ -1305,7 +1306,8 @@ func TestGetEndpoint(t *testing.T) { } for _, ts := range testcases { t.Run(fmt.Sprintf("should create correct endpoint for %s", ts), func(t *testing.T) { - actual := getEndpoint(ts.region) + actual, err := getEndpoint(ts.region) + assert.NoError(t, err) assert.Equal(t, ts.expectedEndpoint, actual) }) } diff --git a/pkg/tsdb/cloudwatch/models/logs_query.go b/pkg/tsdb/cloudwatch/models/logs_query.go index f9ea0c3e0ff..49b9e1187b1 100644 --- a/pkg/tsdb/cloudwatch/models/logs_query.go +++ b/pkg/tsdb/cloudwatch/models/logs_query.go @@ -8,7 +8,7 @@ type LogsQuery struct { dataquery.CloudWatchLogsQuery StartTime *int64 EndTime *int64 - Limit *int64 + Limit *int32 LogGroupName string LogStreamName string QueryId string diff --git a/pkg/tsdb/cloudwatch/models/query_row_response.go b/pkg/tsdb/cloudwatch/models/query_row_response.go index b99913b0a91..8362faf89df 100644 --- a/pkg/tsdb/cloudwatch/models/query_row_response.go +++ b/pkg/tsdb/cloudwatch/models/query_row_response.go @@ -1,32 +1,32 @@ package models import ( - "github.com/aws/aws-sdk-go/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" ) -// queryRowResponse represents the GetMetricData response for a query row in the query editor. +// QueryRowResponse represents the GetMetricData response for a query row in the query editor. type QueryRowResponse struct { - partialDataSet map[string]*cloudwatch.MetricDataResult + partialDataSet map[string]*cloudwatchtypes.MetricDataResult ErrorCodes map[string]bool HasArithmeticError bool ArithmeticErrorMessage string HasPermissionError bool PermissionErrorMessage string - Metrics []*cloudwatch.MetricDataResult - StatusCode string + Metrics []*cloudwatchtypes.MetricDataResult + StatusCode cloudwatchtypes.StatusCode } func NewQueryRowResponse(errors map[string]bool) QueryRowResponse { return QueryRowResponse{ - partialDataSet: make(map[string]*cloudwatch.MetricDataResult), + partialDataSet: make(map[string]*cloudwatchtypes.MetricDataResult), ErrorCodes: errors, HasArithmeticError: false, ArithmeticErrorMessage: "", - Metrics: []*cloudwatch.MetricDataResult{}, + Metrics: []*cloudwatchtypes.MetricDataResult{}, } } -func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) { +func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataResult) { if mdr.Label == nil { return } @@ -34,16 +34,16 @@ func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) if partialData, ok := q.partialDataSet[*mdr.Label]; ok { partialData.Timestamps = append(partialData.Timestamps, mdr.Timestamps...) partialData.Values = append(partialData.Values, mdr.Values...) - q.StatusCode = *mdr.StatusCode - if *mdr.StatusCode != "PartialData" { + q.StatusCode = mdr.StatusCode + if mdr.StatusCode != cloudwatchtypes.StatusCodePartialData { delete(q.partialDataSet, *mdr.Label) } return } q.Metrics = append(q.Metrics, mdr) - q.StatusCode = *mdr.StatusCode - if *mdr.StatusCode == "PartialData" { + q.StatusCode = mdr.StatusCode + if mdr.StatusCode == cloudwatchtypes.StatusCodePartialData { q.partialDataSet[*mdr.Label] = mdr } } diff --git a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go index 80843c9d74e..45474dbfa41 100644 --- a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go +++ b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go @@ -6,11 +6,11 @@ import ( "strconv" ) -const defaultLogGroupLimit = int64(50) +const defaultLogGroupLimit = int32(50) type LogGroupsRequest struct { ResourceRequest - Limit int64 + Limit int32 LogGroupNamePrefix, LogGroupNamePattern *string ListAllLogGroups bool } @@ -45,11 +45,11 @@ func setIfNotEmptyString(paramValue string) *string { return ¶mValue } -func getLimit(limit string) int64 { +func getLimit(limit string) int32 { logGroupLimit := defaultLogGroupLimit intLimit, err := strconv.ParseInt(limit, 10, 64) if err == nil && intLimit > 0 { - logGroupLimit = intLimit + logGroupLimit = int32(intLimit) } return logGroupLimit } diff --git a/pkg/tsdb/cloudwatch/models/resources/types.go b/pkg/tsdb/cloudwatch/models/resources/types.go index baeab0c04b5..f5de61f7f27 100644 --- a/pkg/tsdb/cloudwatch/models/resources/types.go +++ b/pkg/tsdb/cloudwatch/models/resources/types.go @@ -1,6 +1,8 @@ package resources -import "github.com/aws/aws-sdk-go/service/cloudwatch" +import ( + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" +) type Dimension struct { Name string @@ -13,7 +15,7 @@ type ResourceResponse[T any] struct { } type MetricResponse struct { - *cloudwatch.Metric + Metric cloudwatchtypes.Metric AccountId *string `json:"accountId,omitempty"` } diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 85b69a00a8b..2d7db092e48 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -8,7 +8,8 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" @@ -88,7 +89,7 @@ func aggregateResponse(getMetricDataOutputs []*cloudwatch.GetMetricDataOutput) m } } - response.AddMetricDataResult(r) + response.AddMetricDataResult(&r) responseByID[id] = response } } @@ -228,16 +229,9 @@ func buildDataFrames(ctx context.Context, aggregatedResponse models.QueryRowResp } else { labels = getLabels(label, query, false) } - timestamps := []*time.Time{} - points := []*float64{} - for j, t := range metric.Timestamps { - val := metric.Values[j] - timestamps = append(timestamps, t) - points = append(points, val) - } - timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps) - valueField := data.NewField(data.TimeSeriesValueFieldName, labels, points) + timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, metric.Timestamps) + valueField := data.NewField(data.TimeSeriesValueFieldName, labels, metric.Values) // CloudWatch appends the dimensions to the returned label if the query label is not dynamic, so static labels need to be set if hasStaticLabel { diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index d1c8229fa3e..3e46cd6c861 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -7,8 +7,10 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -41,7 +43,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 10) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) + assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) }) t.Run("should have exceeded request limit", func(t *testing.T) { assert.True(t, aggregatedResponse[idA].ErrorCodes["MaxMetricsExceeded"]) @@ -63,7 +65,7 @@ func TestCloudWatchResponseParser(t *testing.T) { aggregatedResponse := aggregateResponse(getMetricDataOutputs) idB := "b" t.Run("should have statuscode is 'PartialData'", func(t *testing.T) { - assert.Equal(t, "PartialData", aggregatedResponse[idB].StatusCode) + assert.Equal(t, cloudwatchtypes.StatusCodePartialData, aggregatedResponse[idB].StatusCode) }) t.Run("should have an arithmetic error and an error message", func(t *testing.T) { assert.True(t, aggregatedResponse[idB].HasArithmeticError) @@ -85,7 +87,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 6) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) + assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) }) }) @@ -153,36 +155,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multi filter", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("lb1|&|lb1"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{ - aws.Float64(10), - aws.Float64(20), - aws.Float64(30), + Values: []float64{ + 10, + 20, + 30, }, - StatusCode: aws.String("Complete"), + StatusCode: cloudwatchtypes.StatusCodeComplete, }, { Id: aws.String("id2"), Label: aws.String("lb2|&|lb2"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{ - aws.Float64(10), - aws.Float64(20), - aws.Float64(30), + Values: []float64{ + 10, + 20, + 30, }, - StatusCode: aws.String("Complete"), + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -223,36 +225,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multiple wildcard filters", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label lb3|&|inst1|&|balancer 1"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{ - aws.Float64(10), - aws.Float64(20), - aws.Float64(30), + Values: []float64{ + 10, + 20, + 30, }, - StatusCode: aws.String("Complete"), + StatusCode: cloudwatchtypes.StatusCodeComplete, }, { Id: aws.String("lb4"), Label: aws.String("some label lb4|&|inst2|&|balancer 2"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{ - aws.Float64(10), - aws.Float64(20), - aws.Float64(30), + Values: []float64{ + 10, + 20, + 30, }, - StatusCode: aws.String("Complete"), + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -294,17 +296,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{}, - StatusCode: aws.String("Complete"), + Values: []float64{}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -337,17 +339,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{}, - StatusCode: aws.String("Complete"), + Values: []float64{}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -387,15 +389,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when not using multi-value dimension filters on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -429,15 +431,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when non-static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -472,15 +474,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -515,15 +517,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when code editor used for `MetricSearch` query add fallback label", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -553,24 +555,24 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no label set and `GROUP BY` clause has multiple fields", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("EC2 vCPU"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, { Id: aws.String("query2"), Label: aws.String("Elastic Loading Balancing ApplicationLoadBalancersPerRegion"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -601,15 +603,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no `GROUP BY` clause", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("cloudwatch-default-label"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -635,15 +637,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("ignore dimensions for raw mode query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []*time.Time{ - aws.Time(timestamp), + Timestamps: []time.Time{ + timestamp, }, - Values: []*float64{aws.Float64(23)}, - StatusCode: aws.String("Complete"), + Values: []float64{23}, + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -675,21 +677,21 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("Parse cloudwatch response", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatch.MetricDataResult{ + Metrics: []*cloudwatchtypes.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("some label"), - Timestamps: []*time.Time{ - aws.Time(timestamp), - aws.Time(timestamp.Add(time.Minute)), - aws.Time(timestamp.Add(3 * time.Minute)), + Timestamps: []time.Time{ + timestamp, + timestamp.Add(time.Minute), + timestamp.Add(3 * time.Minute), }, - Values: []*float64{ - aws.Float64(10), - aws.Float64(20), - aws.Float64(30), + Values: []float64{ + 10, + 20, + 30, }, - StatusCode: aws.String("Complete"), + StatusCode: cloudwatchtypes.StatusCodeComplete, }, }, } @@ -717,9 +719,9 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { assert.Equal(t, "some label", frame.Name) assert.Equal(t, "Time", frame.Fields[0].Name) assert.Equal(t, "lb", frame.Fields[1].Labels["LoadBalancer"]) - assert.Equal(t, 10.0, *frame.Fields[1].At(0).(*float64)) - assert.Equal(t, 20.0, *frame.Fields[1].At(1).(*float64)) - assert.Equal(t, 30.0, *frame.Fields[1].At(2).(*float64)) + assert.Equal(t, 10.0, frame.Fields[1].At(0).(float64)) + assert.Equal(t, 20.0, frame.Fields[1].At(1).(float64)) + assert.Equal(t, 30.0, frame.Fields[1].At(2).(float64)) assert.Equal(t, "Value", frame.Fields[1].Name) assert.Equal(t, "", frame.Fields[1].Config.DisplayName) }) diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields.go b/pkg/tsdb/cloudwatch/routes/log_group_fields.go index 455c2860368..418e234cbbe 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields.go @@ -22,7 +22,7 @@ func LogGroupFieldsHandler(ctx context.Context, pluginCtx backend.PluginContext, return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroupFields, err := service.GetLogGroupFieldsWithContext(ctx, request) + logGroupFields, err := service.GetLogGroupFields(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroupFields error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go index e1c1de40eb4..382244cacd3 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go @@ -31,7 +31,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns 500 if GetLogGroupFields method fails", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) + mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -47,7 +47,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns valid json response if everything is ok", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ + mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ { AccountId: new(string), Value: resources.LogGroupField{ diff --git a/pkg/tsdb/cloudwatch/routes/log_groups.go b/pkg/tsdb/cloudwatch/routes/log_groups.go index 34c9f35ab01..15f5c6d3fd8 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups.go @@ -24,7 +24,7 @@ func LogGroupsHandler(ctx context.Context, pluginCtx backend.PluginContext, reqC return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroups, err := service.GetLogGroupsWithContext(ctx, request) + logGroups, err := service.GetLogGroups(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroups error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_groups_test.go b/pkg/tsdb/cloudwatch/routes/log_groups_test.go index b1f9e53037e..7e1b264a128 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups_test.go @@ -7,10 +7,10 @@ import ( "net/http/httptest" "testing" - "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -29,7 +29,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns 1 log group with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ Value: resources.LogGroup{ Arn: "some arn", Name: "some name", @@ -51,7 +51,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns multiple log groups with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return( + mockLogsService.On("GetLogGroups", mock.Anything).Return( []resources.ResourceResponse[resources.LogGroup]{ { Value: resources.LogGroup{ @@ -97,7 +97,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error when both logGroupPrefix and logGroup Pattern are provided", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -113,7 +113,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix, accountId, and logGroupPattern", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -123,7 +123,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{}, LogGroupNamePrefix: nil, @@ -133,7 +133,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix when both are absent", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -143,7 +143,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: nil, }) @@ -151,7 +151,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes log group limit from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -161,14 +161,14 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 2, }) }) t.Run("passes logGroupPrefix from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -178,7 +178,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: utils.Pointer("some-prefix"), }) @@ -186,7 +186,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -196,7 +196,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePattern: utils.Pointer("some-pattern"), }) @@ -204,7 +204,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -214,7 +214,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("some-account-id")}, }) @@ -222,7 +222,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error if service returns error", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupsWithContext", mock.Anything). + mockLogsService.On("GetLogGroups", mock.Anything). Return([]resources.ResourceResponse[resources.LogGroup]{}, fmt.Errorf("some error")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil diff --git a/pkg/tsdb/cloudwatch/services/accounts.go b/pkg/tsdb/cloudwatch/services/accounts.go index f05b18335a3..94691d258ee 100644 --- a/pkg/tsdb/cloudwatch/services/accounts.go +++ b/pkg/tsdb/cloudwatch/services/accounts.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" + "strings" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/oam" + oam "github.com/aws/aws-sdk-go-v2/service/oam" + oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -23,20 +24,14 @@ func NewAccountsService(oamClient models.OAMAPIProvider) models.AccountsProvider func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ([]resources.ResourceResponse[resources.Account], error) { var nextToken *string - sinks := []*oam.ListSinksItem{} + sinks := []oamtypes.ListSinksItem{} for { - response, err := a.ListSinksWithContext(ctx, &oam.ListSinksInput{NextToken: nextToken}) + response, err := a.ListSinks(ctx, &oam.ListSinksInput{NextToken: nextToken}) if err != nil { - var aerr awserr.Error - if errors.As(err, &aerr) { - switch aerr.Code() { - // unlike many other services, OAM doesn't define this error code. however, it's returned in case calling role/user has insufficient permissions - case "AccessDeniedException": - return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, aerr.Message()) - } + // TODO: this is a bit hacky, figure out how to do it right in v2 + if strings.Contains(err.Error(), "AccessDeniedException") { + return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, err.Error()) } - } - if err != nil { return nil, fmt.Errorf("ListSinks error: %w", err) } @@ -62,7 +57,7 @@ func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ( nextToken = nil for { - links, err := a.ListAttachedLinksWithContext(ctx, &oam.ListAttachedLinksInput{ + links, err := a.ListAttachedLinks(ctx, &oam.ListAttachedLinksInput{ SinkIdentifier: sinkIdentifier, NextToken: nextToken, }) diff --git a/pkg/tsdb/cloudwatch/services/accounts_test.go b/pkg/tsdb/cloudwatch/services/accounts_test.go index dbbadaf660c..ba75ad4a9a0 100644 --- a/pkg/tsdb/cloudwatch/services/accounts_test.go +++ b/pkg/tsdb/cloudwatch/services/accounts_test.go @@ -2,12 +2,14 @@ package services import ( "context" + "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/oam" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/oam" + oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/assert" @@ -18,21 +20,20 @@ import ( func TestHandleGetAccounts(t *testing.T) { t.Run("Should return an error in case of insufficient permissions from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, awserr.New("AccessDeniedException", - "AWS message", nil)) + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, errors.New("AccessDeniedException")) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.Error(t, err) assert.Nil(t, resp) - assert.Equal(t, err.Error(), "access denied. please check your IAM policy: AWS message") + assert.Equal(t, "access denied. please check your IAM policy: AccessDeniedException", err.Error()) assert.ErrorIs(t, err, ErrAccessDeniedException) }) t.Run("Should return an error in case of any error from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -44,7 +45,7 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return empty array in case no monitoring account exists", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, nil) + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -55,26 +56,26 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return one monitoring account (the first) even though ListSinks returns multiple sinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) require.Len(t, resp, 1) assert.True(t, resp[0].Value.IsMonitoringAccount) assert.Equal(t, "Account 1", resp[0].Value.Label) @@ -83,28 +84,28 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should merge the first sink with attached links", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []*oam.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []oamtypes.ListAttachedLinksItem{ {Label: aws.String("Account 10"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10")}, {Label: aws.String("Account 11"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789014:log-group:my-log-group11")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []*oam.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []oamtypes.ListAttachedLinksItem{ {Label: aws.String("Account 12"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group12")}, }, NextToken: nil, @@ -114,8 +115,8 @@ func TestHandleGetAccounts(t *testing.T) { resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) - fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinksWithContext", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinks", 2) expectedAccounts := []resources.ResourceResponse[resources.Account]{ {Value: resources.Account{Id: "123456789012", Label: "Account 1", Arn: "arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1", IsMonitoringAccount: true}}, {Value: resources.Account{Id: "123456789013", Label: "Account 10", Arn: "arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10", IsMonitoringAccount: false}}, @@ -127,34 +128,34 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should call ListAttachedLinks with arn of first sink", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{ + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil).Once() - fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) _, _ = accounts.GetAccountsForCurrentUserOrRole(context.Background()) - fakeOAMClient.AssertCalled(t, "ListAttachedLinksWithContext", &oam.ListAttachedLinksInput{ + fakeOAMClient.AssertCalled(t, "ListAttachedLinks", &oam.ListAttachedLinksInput{ SinkIdentifier: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1"), }) }) t.Run("Should return an error in case of any error from ListAttachedLinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []*oam.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, + fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []oamtypes.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, }, nil) - fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() + fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) diff --git a/pkg/tsdb/cloudwatch/services/list_metrics.go b/pkg/tsdb/cloudwatch/services/list_metrics.go index 3f6088ce32b..e702cc025d1 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics.go @@ -5,8 +5,10 @@ import ( "fmt" "sort" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -30,7 +32,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - metrics, err := l.ListMetricsWithPageLimit(ctx, input) + accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } @@ -38,8 +40,8 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte response := []resources.ResourceResponse[string]{} // remove duplicates dupCheck := make(map[string]struct{}) - for _, metric := range metrics { - for _, dim := range metric.Dimensions { + for _, accountMetric := range accountMetrics { + for _, dim := range accountMetric.Metric.Dimensions { if _, exists := dupCheck[*dim.Name]; exists { continue } @@ -58,7 +60,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte } dupCheck[*dim.Name] = struct{}{} - response = append(response, resources.ResourceResponse[string]{AccountId: metric.AccountId, Value: *dim.Name}) + response = append(response, resources.ResourceResponse[string]{AccountId: accountMetric.AccountId, Value: *dim.Name}) } } @@ -73,15 +75,15 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - metrics, err := l.ListMetricsWithPageLimit(ctx, input) + accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } response := []resources.ResourceResponse[string]{} dupCheck := make(map[string]bool) - for _, metric := range metrics { - for _, dim := range metric.Dimensions { + for _, metric := range accountMetrics { + for _, dim := range metric.Metric.Dimensions { if *dim.Name == r.DimensionKey { if _, exists := dupCheck[*dim.Value]; exists { continue @@ -102,19 +104,19 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error) { input := &cloudwatch.ListMetricsInput{Namespace: aws.String(r.Namespace)} setAccount(input, r.ResourceRequest) - metrics, err := l.ListMetricsWithPageLimit(ctx, input) + accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, err } response := []resources.ResourceResponse[resources.Metric]{} dupCheck := make(map[string]struct{}) - for _, metric := range metrics { - if _, exists := dupCheck[*metric.MetricName]; exists { + for _, accountMetric := range accountMetrics { + if _, exists := dupCheck[*accountMetric.Metric.MetricName]; exists { continue } - dupCheck[*metric.MetricName] = struct{}{} - response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: metric.AccountId, Value: resources.Metric{Name: *metric.MetricName, Namespace: *metric.Namespace}}) + dupCheck[*accountMetric.Metric.MetricName] = struct{}{} + response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: accountMetric.AccountId, Value: resources.Metric{Name: *accountMetric.Metric.MetricName, Namespace: *accountMetric.Metric.Namespace}}) } return response, nil @@ -122,7 +124,7 @@ func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resour func setDimensionFilter(input *cloudwatch.ListMetricsInput, dimensionFilter []*resources.Dimension) { for _, dimension := range dimensionFilter { - df := &cloudwatch.DimensionFilter{ + df := cloudwatchtypes.DimensionFilter{ Name: aws.String(dimension.Name), } if dimension.Value != "" { diff --git a/pkg/tsdb/cloudwatch/services/list_metrics_test.go b/pkg/tsdb/cloudwatch/services/list_metrics_test.go index 7f832878443..daf5c9f458b 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics_test.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics_test.go @@ -4,8 +4,10 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -18,20 +20,20 @@ const useLinkedAccountsId = "all" var metricResponse = []resources.MetricResponse{ { - Metric: &cloudwatch.Metric{ + Metric: cloudwatchtypes.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.Dimension{ + Dimensions: []cloudwatchtypes.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-1234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, }, }, }, { - Metric: &cloudwatch.Metric{ + Metric: cloudwatchtypes.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.Dimension{ + Dimensions: []cloudwatchtypes.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-5234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg")}, @@ -39,10 +41,10 @@ var metricResponse = []resources.MetricResponse{ }, }, { - Metric: &cloudwatch.Metric{ + Metric: cloudwatchtypes.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.Dimension{ + Dimensions: []cloudwatchtypes.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-64234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t3.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg2")}, @@ -86,7 +88,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -101,7 +103,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, @@ -114,7 +116,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { MetricName: "", DimensionFilter: []*resources.Dimension{{Name: "InstanceId", Value: ""}}, }, - listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}}, + listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}}, }, } @@ -163,7 +165,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -178,7 +180,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, diff --git a/pkg/tsdb/cloudwatch/services/log_groups.go b/pkg/tsdb/cloudwatch/services/log_groups.go index 1bbc99a67bd..ad0011b6456 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups.go +++ b/pkg/tsdb/cloudwatch/services/log_groups.go @@ -3,9 +3,9 @@ package services import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -20,9 +20,9 @@ func NewLogGroupsService(logsClient models.CloudWatchLogsAPIProvider, isCrossAcc return &LogGroupsService{logGroupsAPI: logsClient, isCrossAccountEnabled: isCrossAccountEnabled} } -func (s *LogGroupsService) GetLogGroupsWithContext(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { input := &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(req.Limit), + Limit: aws.Int32(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, } @@ -33,13 +33,13 @@ func (s *LogGroupsService) GetLogGroupsWithContext(ctx context.Context, req reso } if !req.IsTargetingAllAccounts() { // TODO: accept more than one account id in search - input.AccountIdentifiers = []*string{req.AccountId} + input.AccountIdentifiers = []string{*req.AccountId} } } result := []resources.ResourceResponse[resources.LogGroup]{} for { - response, err := s.logGroupsAPI.DescribeLogGroupsWithContext(ctx, input) + response, err := s.logGroupsAPI.DescribeLogGroups(ctx, input) if err != nil || response == nil { return nil, err } @@ -63,7 +63,7 @@ func (s *LogGroupsService) GetLogGroupsWithContext(ctx context.Context, req reso return result, nil } -func (s *LogGroupsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { input := &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupName: aws.String(request.LogGroupName), } @@ -73,7 +73,7 @@ func (s *LogGroupsService) GetLogGroupFieldsWithContext(ctx context.Context, req // input.LogGroupName = nil // } - getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFieldsWithContext(ctx, input) + getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFields(ctx, input) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (s *LogGroupsService) GetLogGroupFieldsWithContext(ctx context.Context, req result = append(result, resources.ResourceResponse[resources.LogGroupField]{ Value: resources.LogGroupField{ Name: *logGroupField.Name, - Percent: *logGroupField.Percent, + Percent: int64(logGroupField.Percent), }, }) } diff --git a/pkg/tsdb/cloudwatch/services/log_groups_test.go b/pkg/tsdb/cloudwatch/services/log_groups_test.go index 6b9a09170c3..992044bc1be 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/services/log_groups_test.go @@ -5,11 +5,14 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -17,9 +20,9 @@ import ( func TestGetLogGroups(t *testing.T) { t.Run("Should map log groups response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return( + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return( &cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []*cloudwatchlogs.LogGroup{ + LogGroups: []cloudwatchlogstypes.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:333:log-group:group_c"), LogGroupName: utils.Pointer("group_c")}, @@ -27,7 +30,7 @@ func TestGetLogGroups(t *testing.T) { }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ @@ -48,10 +51,10 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should return an empty error if api doesn't return any data", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{}, resp) @@ -60,41 +63,41 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should only use LogGroupNamePrefix even if LogGroupNamePattern passed in resource call", func(t *testing.T) { // TODO: use LogGroupNamePattern when we have accounted for its behavior, still a little unexpected at the moment mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ Limit: 0, LogGroupNamePrefix: utils.Pointer("test"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("test"), }) }) t.Run("Should call api without LogGroupNamePrefix nor LogGroupNamePattern if not passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(0), }) }) t.Run("Should return an error when API returns error", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, fmt.Errorf("some error")) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) assert.Error(t, err) assert.Equal(t, "some error", err.Error()) @@ -108,21 +111,21 @@ func TestGetLogGroups(t *testing.T) { ListAllLogGroups: false, } - mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(req.Limit), + mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []*cloudwatchlogs.LogGroup{ + LogGroups: []cloudwatchlogstypes.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: aws.String("next_token"), }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupsWithContext(context.Background(), req) + resp, err := service.GetLogGroups(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 1) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 1) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -140,30 +143,30 @@ func TestGetLogGroups(t *testing.T) { } // first call - mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(req.Limit), + mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []*cloudwatchlogs.LogGroup{ + LogGroups: []cloudwatchlogstypes.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: utils.Pointer("token"), }, nil) // second call - mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(req.Limit), + mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, NextToken: utils.Pointer("token"), }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []*cloudwatchlogs.LogGroup{ + LogGroups: []cloudwatchlogstypes.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupsWithContext(context.Background(), req) + resp, err := service.GetLogGroups(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 2) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 2) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -180,36 +183,36 @@ func TestGetLogGroups(t *testing.T) { func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts or accountId if isCrossAccountEnabled is set to false", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should replace LogGroupNamePrefix if LogGroupNamePattern passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), LogGroupNamePattern: utils.Pointer("pattern"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []*string{utils.Pointer("accountId")}, - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []string{"accountId"}, + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("pattern"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -217,34 +220,34 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should includeLinkedAccounts,and accountId if isCrossAccountEnabled is set to true", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(0), IncludeLinkedAccounts: utils.Pointer(true), - AccountIdentifiers: []*string{utils.Pointer("accountId")}, + AccountIdentifiers: []string{"accountId"}, }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []*string{utils.Pointer("accountId")}, - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []string{"accountId"}, + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("prefix"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -252,26 +255,26 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts, or accountId if accountId is nil", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: utils.Pointer(int64(0)), + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{ AccountId: utils.Pointer("accountId"), }, @@ -279,10 +282,10 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []*string{utils.Pointer("accountId")}, + mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []string{"accountId"}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: utils.Pointer(int64(0)), + Limit: aws.Int32(0), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) @@ -291,24 +294,24 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { func TestGetLogGroupFields(t *testing.T) { t.Run("Should map log group fields response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []*cloudwatchlogs.LogGroupField{ + LogGroupFields: []cloudwatchlogstypes.LogGroupField{ { - Name: utils.Pointer("field1"), - Percent: utils.Pointer(int64(10)), + Name: aws.String("field1"), + Percent: 10, }, { - Name: utils.Pointer("field2"), - Percent: utils.Pointer(int64(10)), + Name: aws.String("field2"), + Percent: 10, }, { - Name: utils.Pointer("field3"), - Percent: utils.Pointer(int64(10)), + Name: aws.String("field3"), + Percent: 10, }, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{}) + resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroupField]{ @@ -356,16 +359,16 @@ func TestGetLogGroupFields(t *testing.T) { // remove this test once the above test is uncommented t.Run("Should only set LogGroupName as api input in case both LogGroupName and LogGroupARN are specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "logGroupARN", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) @@ -375,16 +378,16 @@ func TestGetLogGroupFields(t *testing.T) { t.Run("Should only set LogGroupName as api input in case only LogGroupName is specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) diff --git a/pkg/tsdb/cloudwatch/services/regions.go b/pkg/tsdb/cloudwatch/services/regions.go index 2f8fc345e07..e0de066389b 100644 --- a/pkg/tsdb/cloudwatch/services/regions.go +++ b/pkg/tsdb/cloudwatch/services/regions.go @@ -4,7 +4,9 @@ import ( "context" "sort" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -23,7 +25,7 @@ func NewRegionsService(ec2client models.EC2APIProvider, logger log.Logger) model } } -func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []*ec2.Region) { +func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []ec2types.Region) { for _, region := range ec2Regions { if _, ok := regions[*region.RegionName]; !ok { regions[*region.RegionName] = struct{}{} @@ -36,7 +38,7 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe result := make([]resources.ResourceResponse[resources.Region], 0) - ec2Regions, err := r.DescribeRegionsWithContext(ctx, &ec2.DescribeRegionsInput{}) + ec2Regions, err := r.DescribeRegions(ctx, &ec2.DescribeRegionsInput{}) // we ignore this error and always send default regions // we only fetch incase a user has enabled additional regions // but we still log it in case the user is expecting to fetch regions specific to their account and are unable to @@ -44,7 +46,9 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe r.Error("Failed to get regions: ", "error", err) } - mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) + if ec2Regions != nil { + mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) + } for region := range regions { result = append(result, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/services/regions_test.go b/pkg/tsdb/cloudwatch/services/regions_test.go index bb559ea5907..b352a493ba8 100644 --- a/pkg/tsdb/cloudwatch/services/regions_test.go +++ b/pkg/tsdb/cloudwatch/services/regions_test.go @@ -4,7 +4,9 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -17,14 +19,14 @@ var testLogger = log.New().With("logger", "test.logger") func TestRegions(t *testing.T) { t.Run("returns regions from the api and merges them with default regions", func(t *testing.T) { mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []*ec2.Region{ + Regions: []ec2types.Region{ { RegionName: utils.Pointer("earth-1"), }, }, } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, nil) + ec2Mock.On("DescribeRegions").Return(mockRegions, nil) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ @@ -42,9 +44,9 @@ func TestRegions(t *testing.T) { t.Run("always returns default regions, even if fetch fails", func(t *testing.T) { ec2Mock := &mocks.EC2Mock{} mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []*ec2.Region{}, + Regions: []ec2types.Region{}, } - ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, assert.AnError) + ec2Mock.On("DescribeRegions").Return(mockRegions, assert.AnError) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/sort_frame_test.go b/pkg/tsdb/cloudwatch/sort_frame_test.go index cc7408d9c67..2f136431d3e 100644 --- a/pkg/tsdb/cloudwatch/sort_frame_test.go +++ b/pkg/tsdb/cloudwatch/sort_frame_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" diff --git a/pkg/tsdb/cloudwatch/test_utils.go b/pkg/tsdb/cloudwatch/test_utils.go index 5d56ee96222..e4ac75e7840 100644 --- a/pkg/tsdb/cloudwatch/test_utils.go +++ b/pkg/tsdb/cloudwatch/test_utils.go @@ -4,17 +4,18 @@ import ( "context" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" - "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" + "github.com/aws/smithy-go" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" + + "github.com/grafana/grafana-aws-sdk/pkg/awsauth" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -26,8 +27,6 @@ import ( ) type fakeCWLogsClient struct { - cloudwatchlogsiface.CloudWatchLogsAPI - calls logsQueryCalls logGroups []cloudwatchlogs.DescribeLogGroupsOutput @@ -38,83 +37,104 @@ type fakeCWLogsClient struct { } type logsQueryCalls struct { - startQueryWithContext []*cloudwatchlogs.StartQueryInput - getEventsWithContext []*cloudwatchlogs.GetLogEventsInput - describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput + startQuery []*cloudwatchlogs.StartQueryInput + getEvents []*cloudwatchlogs.GetLogEventsInput + describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput } -func (m *fakeCWLogsClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { +func (m *fakeCWLogsClient) GetQueryResults(_ context.Context, _ *cloudwatchlogs.GetQueryResultsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { return &m.queryResults, nil } -func (m *fakeCWLogsClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { - m.calls.startQueryWithContext = append(m.calls.startQueryWithContext, input) +func (m *fakeCWLogsClient) StartQuery(_ context.Context, input *cloudwatchlogs.StartQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { + m.calls.startQuery = append(m.calls.startQuery, input) return &cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil } -func (m *fakeCWLogsClient) StopQueryWithContext(ctx context.Context, input *cloudwatchlogs.StopQueryInput, option ...request.Option) (*cloudwatchlogs.StopQueryOutput, error) { +func (m *fakeCWLogsClient) StopQuery(_ context.Context, _ *cloudwatchlogs.StopQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { return &cloudwatchlogs.StopQueryOutput{ - Success: aws.Bool(true), + Success: true, }, nil } type mockLogsSyncClient struct { - cloudwatchlogsiface.CloudWatchLogsAPI - mock.Mock } -func (m *mockLogsSyncClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { - args := m.Called(ctx, input, option) +func (m *mockLogsSyncClient) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { + return nil, nil +} + +func (m *mockLogsSyncClient) GetLogEvents(context.Context, *cloudwatchlogs.GetLogEventsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { + return nil, nil +} + +func (m *mockLogsSyncClient) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + return nil, nil +} + +func (m *mockLogsSyncClient) GetQueryResults(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { + args := m.Called(ctx, input, optFns) return args.Get(0).(*cloudwatchlogs.GetQueryResultsOutput), args.Error(1) } -func (m *mockLogsSyncClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { - args := m.Called(ctx, input, option) +func (m *mockLogsSyncClient) StartQuery(ctx context.Context, input *cloudwatchlogs.StartQueryInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { + args := m.Called(ctx, input, optFns) return args.Get(0).(*cloudwatchlogs.StartQueryOutput), args.Error(1) } -func (m *fakeCWLogsClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (m *fakeCWLogsClient) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { m.calls.describeLogGroups = append(m.calls.describeLogGroups, input) output := &m.logGroups[m.logGroupsIndex] m.logGroupsIndex++ return output, nil } -func (m *fakeCWLogsClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (m *fakeCWLogsClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return &m.logGroupFields, nil } -func (m *fakeCWLogsClient) GetLogEventsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { - m.calls.getEventsWithContext = append(m.calls.getEventsWithContext, input) +func (m *fakeCWLogsClient) GetLogEvents(_ context.Context, input *cloudwatchlogs.GetLogEventsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { + m.calls.getEvents = append(m.calls.getEvents, input) return &cloudwatchlogs.GetLogEventsOutput{ - Events: []*cloudwatchlogs.OutputLogEvent{}, + Events: []cloudwatchlogstypes.OutputLogEvent{}, }, nil } type fakeCWAnnotationsClient struct { - cloudwatchiface.CloudWatchAPI calls annontationsQueryCalls describeAlarmsForMetricOutput *cloudwatch.DescribeAlarmsForMetricOutput describeAlarmsOutput *cloudwatch.DescribeAlarmsOutput } +func (c *fakeCWAnnotationsClient) DescribeAlarmHistory(ctx context.Context, input *cloudwatch.DescribeAlarmHistoryInput, f ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmHistoryOutput, error) { + return nil, nil +} + +func (c *fakeCWAnnotationsClient) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, f ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { + return nil, nil +} + +func (c *fakeCWAnnotationsClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, f ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { + return nil, nil +} + type annontationsQueryCalls struct { describeAlarmsForMetric []*cloudwatch.DescribeAlarmsForMetricInput describeAlarms []*cloudwatch.DescribeAlarmsInput } -func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(params *cloudwatch.DescribeAlarmsForMetricInput) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(_ context.Context, params *cloudwatch.DescribeAlarmsForMetricInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { c.calls.describeAlarmsForMetric = append(c.calls.describeAlarmsForMetric, params) return c.describeAlarmsForMetricOutput, nil } -func (c *fakeCWAnnotationsClient) DescribeAlarms(params *cloudwatch.DescribeAlarmsInput) (*cloudwatch.DescribeAlarmsOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarms(_ context.Context, params *cloudwatch.DescribeAlarmsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error) { c.calls.describeAlarms = append(c.calls.describeAlarms, params) return c.describeAlarmsOutput, nil @@ -122,16 +142,14 @@ func (c *fakeCWAnnotationsClient) DescribeAlarms(params *cloudwatch.DescribeAlar // Please use mockEC2Client above, we are slowly migrating towards using testify's mocks only type oldEC2Client struct { - ec2iface.EC2API - regions []string - reservations []*ec2.Reservation + reservations []ec2types.Reservation } -func (c oldEC2Client) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, option ...request.Option) (*ec2.DescribeRegionsOutput, error) { - regions := []*ec2.Region{} +func (c oldEC2Client) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { + regions := []ec2types.Region{} for _, region := range c.regions { - regions = append(regions, &ec2.Region{ + regions = append(regions, ec2types.Region{ RegionName: aws.String(region), }) } @@ -140,11 +158,10 @@ func (c oldEC2Client) DescribeRegionsWithContext(ctx aws.Context, in *ec2.Descri }, nil } -func (c oldEC2Client) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, - fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { - reservations := []*ec2.Reservation{} +func (c oldEC2Client) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { + reservations := []ec2types.Reservation{} for _, r := range c.reservations { - instances := []*ec2.Instance{} + instances := []ec2types.Instance{} for _, inst := range r.Instances { if len(in.InstanceIds) == 0 { instances = append(instances, inst) @@ -152,97 +169,85 @@ func (c oldEC2Client) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2 } for _, id := range in.InstanceIds { - if *inst.InstanceId == *id { + if *inst.InstanceId == id { instances = append(instances, inst) } } } - reservation := &ec2.Reservation{Instances: instances} + reservation := ec2types.Reservation{Instances: instances} reservations = append(reservations, reservation) } - fn(&ec2.DescribeInstancesOutput{ + return &ec2.DescribeInstancesOutput{ Reservations: reservations, - }, true) - return nil + }, nil } type fakeRGTAClient struct { - resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI - - tagMapping []*resourcegroupstaggingapi.ResourceTagMapping + tagMapping []resourcegroupstaggingapitypes.ResourceTagMapping } -func (c fakeRGTAClient) GetResourcesPagesWithContext(ctx context.Context, in *resourcegroupstaggingapi.GetResourcesInput, - fn func(*resourcegroupstaggingapi.GetResourcesOutput, bool) bool, opts ...request.Option) error { - fn(&resourcegroupstaggingapi.GetResourcesOutput{ +func (c fakeRGTAClient) GetResources(_ context.Context, _ *resourcegroupstaggingapi.GetResourcesInput, _ ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) { + return &resourcegroupstaggingapi.GetResourcesOutput{ ResourceTagMappingList: c.tagMapping, - }, true) - return nil + }, nil } type fakeCheckHealthClient struct { - listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error - describeLogGroups func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) + listMetricsFunction func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) + describeLogGroupsFunction func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) + + models.CWClient } -func (c fakeCheckHealthClient) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { - if c.listMetricsPages != nil { - return c.listMetricsPages(input, fn) +func (c fakeCheckHealthClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { + if c.listMetricsFunction != nil { + return c.listMetricsFunction(ctx, input) } - return nil + return &cloudwatch.ListMetricsOutput{}, nil } -func (c fakeCheckHealthClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - if c.describeLogGroups != nil { - return c.describeLogGroups(input) +func (c fakeCheckHealthClient) DescribeLogGroups(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + if c.describeLogGroupsFunction != nil { + return c.describeLogGroupsFunction(ctx, input) } return nil, nil } -func (c fakeCheckHealthClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (c fakeCheckHealthClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return nil, nil } -func testInstanceManager(pageLimit int) instancemgmt.InstanceManager { - return datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-1", - }, - GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, - }, - sessions: &fakeSessionCache{}, - tagValueCache: cache.New(0, 0)}, nil - })) +func testInstanceManagerWithSettings(settings models.CloudWatchSettings, awsAuthShouldFail bool) instancemgmt.InstanceManager { + return datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{ + Settings: settings, + AWSConfigProvider: awsauth.NewFakeConfigProvider(awsAuthShouldFail), + tagValueCache: cache.New(0, 0), + }, nil + }) } -func defaultTestInstanceManager() instancemgmt.InstanceManager { - return testInstanceManager(1000) +func testInstanceManager(pageLimit int, getAWSConfigShouldFail bool) instancemgmt.InstanceManager { + return testInstanceManagerWithSettings(models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-1", + AuthType: awsds.AuthTypeKeys, + AccessKey: "nothing", + SecretKey: "nowhere", + }, + GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, + }, getAWSConfigShouldFail) } -type mockSessionCache struct { - mock.Mock +func defaultTestInstanceManager() instancemgmt.InstanceManager { + return testInstanceManager(1000, false) } -func (c *mockSessionCache) GetSessionWithAuthSettings(config awsds.GetSessionConfig, auth awsds.AuthSettings) (*session.Session, error) { - args := c.Called(config) - return args.Get(0).(*session.Session), args.Error(1) +type FakeCredentialsProvider struct { } -type fakeSessionCache struct { - getSessionWithAuthSettings func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) - calledRegions []string -} - -func (s *fakeSessionCache) GetSessionWithAuthSettings(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { - s.calledRegions = append(s.calledRegions, c.Settings.Region) - - if s.getSessionWithAuthSettings != nil { - return s.getSessionWithAuthSettings(c, a) - } - return &session.Session{ - Config: &aws.Config{}, - }, nil +func (fcp *FakeCredentialsProvider) Retrieve(_ context.Context) (aws.Credentials, error) { + return aws.Credentials{}, nil } type mockedCallResourceResponseSenderForOauth struct { @@ -254,25 +259,25 @@ func (s *mockedCallResourceResponseSenderForOauth) Send(resp *backend.CallResour return nil } -type fakeAWSError struct { +type fakeSmithyError struct { code string message string } -func (e fakeAWSError) OrigErr() error { - return nil +func (f fakeSmithyError) Error() string { + return f.message } -func (e fakeAWSError) Error() string { - return e.message +func (f fakeSmithyError) ErrorCode() string { + return f.code } -func (e fakeAWSError) Code() string { - return e.code +func (f fakeSmithyError) ErrorMessage() string { + return f.message } -func (e fakeAWSError) Message() string { - return e.message +func (f fakeSmithyError) ErrorFault() smithy.ErrorFault { + return 0 } func contextWithFeaturesEnabled(enabled ...string) context.Context { diff --git a/pkg/tsdb/cloudwatch/time_series_query_test.go b/pkg/tsdb/cloudwatch/time_series_query_test.go index c2e65bb5434..e51f0c58551 100644 --- a/pkg/tsdb/cloudwatch/time_series_query_test.go +++ b/pkg/tsdb/cloudwatch/time_series_query_test.go @@ -6,16 +6,12 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" - "github.com/grafana/grafana-aws-sdk/pkg/awsds" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" - "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" - "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -23,6 +19,7 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -36,19 +33,19 @@ func TestTimeSeriesQuery(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &api } t.Run("Custom metrics", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []*cloudwatch.MetricDataResult{ + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ + MetricDataResults: []cloudwatchtypes.MetricDataResult{ { - StatusCode: aws.String("Complete"), Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, + StatusCode: "Complete", Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []float64{1.0}, Timestamps: []time.Time{now}, }, { - StatusCode: aws.String("Complete"), Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, + StatusCode: "Complete", Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []float64{1.0}, Timestamps: []time.Time{now}, }}}, nil) im := defaultTestInstanceManager() @@ -145,28 +142,23 @@ func TestTimeSeriesQuery(t *testing.T) { func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMetricData_is_called_once_per_grouping_of_queries_by_region(t *testing.T) { /* TODO: This test aims to verify the logic to group regions which has been extracted from ParseMetricDataQueries. It should be replaced by a test at a lower level when grouping by regions is incorporated into a separate business logic layer */ + // FIXME: this test is broken - it only works because we're recovering from the panic that the Mock + // produces - see time_series_query.go line 78. If that recover is commented out, the test fails. + t.Skip("skipping broken test") origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient }) var mockMetricClient mocks.MetricsAPI - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &mockMetricClient } - t.Run("Queries with the same region should call GetSessionWithAuthSettings with that region 1 time and call GetMetricDataWithContext 1 time", func(t *testing.T) { - mockSessionCache := &mockSessionCache{} - mockSessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( - func(config awsds.GetSessionConfig) bool { - return config.Settings.Region == "us-east-1" - })). // region from queries is asserted here - Return(&session.Session{Config: &aws.Config{}}, nil).Once() - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: mockSessionCache}, nil - }) + t.Run("Queries with the same region should call GetMetricData 1 time", func(t *testing.T) { + im := defaultTestInstanceManager() mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -202,31 +194,16 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) - mockSessionCache.AssertExpectations(t) // method is defined to only return "Once()", // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 1) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 1) // GetMetricData is asserted to have been called 1 time for the 1 region present in the queries }) - t.Run("3 queries with 2 regions calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { - sessionCache := &mockSessionCache{} - sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( - func(config awsds.GetSessionConfig) bool { - return config.Settings.Region == "us-east-1" - })). - Return(&session.Session{Config: &aws.Config{}}, nil).Once() - sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( - func(config awsds.GetSessionConfig) bool { - return config.Settings.Region == "us-east-2" - })). - Return(&session.Session{Config: &aws.Config{}}, nil).Once() - - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil - }) + t.Run("3 queries with 2 regions calls GetMetricData 2 times", func(t *testing.T) { + im := defaultTestInstanceManager() mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -274,26 +251,16 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) - sessionCache.AssertExpectations(t) // method is defined to only return "Once()" for each region. // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each group of regions (2 regions total) }) - t.Run("3 queries with 2 time ranges calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { - sessionCache := &mockSessionCache{} - sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( - func(config awsds.GetSessionConfig) bool { - return config.Settings.Region == "us-east-2" - })). - Return(&session.Session{Config: &aws.Config{}}, nil).Times(2) - - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil - }) + t.Run("3 queries with 2 time ranges calls GetMetricData 2 times", func(t *testing.T) { + im := defaultTestInstanceManager() mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) + mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -341,8 +308,7 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) - sessionCache.AssertExpectations(t) // method is defined to return twice (once for each batch) - mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each time range (2 time ranges total) }) } @@ -408,7 +374,7 @@ func newTestQuery(t testing.TB, p queryParameters) json.RawMessage { return marshalled } -func Test_QueryData_timeSeriesQuery_GetMetricDataWithContext(t *testing.T) { +func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient @@ -416,17 +382,15 @@ func Test_QueryData_timeSeriesQuery_GetMetricDataWithContext(t *testing.T) { var api mocks.MetricsAPI - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &api } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil - }) + im := defaultTestInstanceManager() t.Run("passes query label as GetMetricData label", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) query := newTestQuery(t, queryParameters{ Label: aws.String("${PROP('Period')} some words ${PROP('Dim.InstanceId')}"), @@ -465,7 +429,7 @@ func Test_QueryData_timeSeriesQuery_GetMetricDataWithContext(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -499,21 +463,21 @@ func Test_QueryData_response_data_frame_name_is_always_response_label(t *testing NewCWClient = origNewCWClient }) - api := mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ - {MetricName: aws.String(""), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, + api := mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ + {MetricName: aws.String(""), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, }} - api.On("ListMetricsPagesWithContext").Return(nil) + api.On("ListMetricsPages").Return(nil) - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &api } labelFromGetMetricData := "some label" - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything). + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything). Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []*cloudwatch.MetricDataResult{ - {StatusCode: aws.String("Complete"), Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), - Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{{}}}, + MetricDataResults: []cloudwatchtypes.MetricDataResult{ + {StatusCode: "Complete", Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), + Values: []float64{1.0}, Timestamps: []time.Time{{}}}, }}, nil) im := defaultTestInstanceManager() @@ -666,14 +630,14 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewCWClient = func(aws.Config) models.CWClient { return &api } im := defaultTestInstanceManager() t.Run("should call GetMetricDataInput with AccountId nil when no AccountId is provided", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -714,7 +678,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId nil when feature flag is false", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -755,7 +719,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId in a MetricStat query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -796,7 +760,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should GetMetricDataInput with AccountId in an inferred search expression query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ From d4a4aac8d109826416491bbf9ae3141edcf5940d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Jamr=C3=B3z?= Date: Mon, 24 Mar 2025 11:47:56 +0100 Subject: [PATCH 23/86] TraceView: Fix links menu position (#102693) --- .../TraceView/components/TraceTimelineViewer/SpanLinks.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx index ea0239f8df6..4f2f68f9c5b 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx @@ -61,8 +61,8 @@ export const SpanLinksMenu = ({ links, datasourceType, color }: SpanLinksProps) onClick={(e) => { setIsMenuOpen(true); setMenuPosition({ - x: e.pageX, - y: e.pageY, + x: e.clientX, + y: e.clientY, }); }} className={styles.button} From 00db0cf6e6ace36cf25d0af665eeb7f895914485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0tibran=C3=BD?= Date: Mon, 24 Mar 2025 12:16:12 +0100 Subject: [PATCH 24/86] Remove dependency from OSS to enterprise packages by moving Spanner functions to xorm. (#102692) * Remove dependency from OSS to enterprise packages by moving Spanner functions to xorm. --- go.mod | 2 +- .../sqlstore/migrator/spanner_dialect.go | 3 +- pkg/util/xorm/dialect_spanner.go | 36 +++++++++++++++++-- pkg/util/xorm/go.mod | 4 +-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 63ce71d455a..6be820fd92c 100644 --- a/go.mod +++ b/go.mod @@ -560,12 +560,12 @@ require ( ) require ( + github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2 // @grafana/grafana-search-and-storage github.com/open-feature/go-sdk v1.14.1 // @grafana/grafana-backend-group github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.3 // @grafana/grafana-backend-group ) require ( - github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect diff --git a/pkg/services/sqlstore/migrator/spanner_dialect.go b/pkg/services/sqlstore/migrator/spanner_dialect.go index 950aabf2f2a..1bd540ea4b1 100644 --- a/pkg/services/sqlstore/migrator/spanner_dialect.go +++ b/pkg/services/sqlstore/migrator/spanner_dialect.go @@ -17,7 +17,6 @@ import ( "google.golang.org/grpc/codes" "xorm.io/core" - spannerext "github.com/grafana/grafana/pkg/extensions/spanner" "xorm.io/xorm" _ "embed" @@ -291,7 +290,7 @@ func (s *SpannerDialect) executeDDLStatements(ctx context.Context, engine *xorm. return err } - opts := spannerext.SpannerConnectorConfigToClientOptions(cfg) + opts := xorm.SpannerConnectorConfigToClientOptions(cfg) databaseAdminClient, err := database.NewDatabaseAdminClient(ctx, opts...) if err != nil { diff --git a/pkg/util/xorm/dialect_spanner.go b/pkg/util/xorm/dialect_spanner.go index df4b0e3965e..2460f4228dd 100644 --- a/pkg/util/xorm/dialect_spanner.go +++ b/pkg/util/xorm/dialect_spanner.go @@ -10,9 +10,10 @@ import ( _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" + "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "xorm.io/core" - - spannerext "github.com/grafana/grafana/pkg/extensions/spanner" ) func init() { @@ -381,7 +382,7 @@ func (s *spanner) CreateSequenceGenerator(db *sql.DB) (SequenceGenerator, error) return nil, err } - if spannerext.UsePlainText(connectorConfig) { + if UsePlainText(connectorConfig) { // Plain-text means we're either using spannertest or Spanner emulator. // Switch to fake in-memory sequence number generator in that case. // @@ -393,3 +394,32 @@ func (s *spanner) CreateSequenceGenerator(db *sql.DB) (SequenceGenerator, error) return newSequenceGenerator(db), nil } + +func UsePlainText(connectorConfig spannerdriver.ConnectorConfig) bool { + if strval, ok := connectorConfig.Params["useplaintext"]; ok { + if val, err := strconv.ParseBool(strval); err == nil { + return val + } + } + return false +} + +// SpannerConnectorConfigToClientOptions is adapted from https://github.com/googleapis/go-sql-spanner/blob/main/driver.go#L341-L477, from version 1.11.1. +func SpannerConnectorConfigToClientOptions(connectorConfig spannerdriver.ConnectorConfig) []option.ClientOption { + var opts []option.ClientOption + if connectorConfig.Host != "" { + opts = append(opts, option.WithEndpoint(connectorConfig.Host)) + } + if strval, ok := connectorConfig.Params["credentials"]; ok { + opts = append(opts, option.WithCredentialsFile(strval)) + } + if strval, ok := connectorConfig.Params["credentialsjson"]; ok { + opts = append(opts, option.WithCredentialsJSON([]byte(strval))) + } + if UsePlainText(connectorConfig) { + opts = append(opts, + option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())), + option.WithoutAuthentication()) + } + return opts +} diff --git a/pkg/util/xorm/go.mod b/pkg/util/xorm/go.mod index e9406624f26..10d79f8947a 100644 --- a/pkg/util/xorm/go.mod +++ b/pkg/util/xorm/go.mod @@ -7,6 +7,8 @@ require ( github.com/googleapis/go-sql-spanner v1.11.1 github.com/mattn/go-sqlite3 v1.14.22 github.com/stretchr/testify v1.10.0 + google.golang.org/api v0.220.0 + google.golang.org/grpc v1.70.0 xorm.io/builder v0.3.6 xorm.io/core v0.7.3 ) @@ -55,11 +57,9 @@ require ( golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - google.golang.org/api v0.220.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect - google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) From 0bedd578123c384c8a0a7cb684ff43dea0a3900c Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 24 Mar 2025 11:41:45 +0000 Subject: [PATCH 25/86] Chore: Auto mark up some more areas for translations (#102479) * markup api-keys * markup auth-config * auto markup connections * markup explore * translation extractions * fix unit tests --- .betterer.results | 302 +++--------------- .../features/api-keys/ApiKeysActionBar.tsx | 7 +- public/app/features/api-keys/ApiKeysPage.tsx | 20 +- public/app/features/api-keys/ApiKeysTable.tsx | 23 +- .../api-keys/MigrateToServiceAccountsCard.tsx | 30 +- .../app/features/auth-config/AuthDrawer.tsx | 20 +- .../auth-config/AuthProvidersListPage.tsx | 3 +- .../auth-config/ProviderConfigForm.tsx | 22 +- .../components/ConfigureAuthCTA.tsx | 13 +- .../components/ServerDiscoveryModal.tsx | 16 +- .../ConnectionsRedirectNotice.tsx | 13 +- .../NoAccessModal/NoAccessModal.tsx | 11 +- .../tabs/ConnectData/Search/Search.tsx | 2 +- .../ContentOutlineItemButton.tsx | 6 +- .../explore/CorrelationEditorModeBar.tsx | 5 +- .../features/explore/CorrelationHelper.tsx | 16 +- .../CorrelationTransformationAddModal.tsx | 23 +- .../CorrelationUnsavedChangesModal.tsx | 9 +- public/app/features/explore/Explore.tsx | 45 ++- .../explore/ExploreRunQueryButton.tsx | 9 +- .../app/features/explore/ExploreToolbar.tsx | 4 +- .../app/features/explore/LiveTailButton.tsx | 24 +- public/app/features/explore/Logs/LiveLogs.tsx | 5 +- public/app/features/explore/Logs/Logs.tsx | 38 ++- .../features/explore/Logs/LogsContainer.tsx | 3 +- .../app/features/explore/Logs/LogsMetaRow.tsx | 8 +- .../features/explore/Logs/LogsSamplePanel.tsx | 22 +- .../explore/Logs/LogsTableEmptyFields.tsx | 7 +- .../explore/Logs/LogsTableMultiSelect.tsx | 5 +- .../explore/Logs/LogsTableNavField.tsx | 5 +- .../features/explore/Logs/LogsTableWrap.tsx | 8 +- .../explore/Logs/LogsVolumePanelList.tsx | 32 +- .../explore/NoDataSourceCallToAction.tsx | 12 +- .../PrometheusListView/RawListItem.tsx | 3 +- .../explore/SupplementaryResultError.tsx | 3 +- .../explore/TraceView/TraceView.test.tsx | 8 +- .../features/explore/TraceView/TraceView.tsx | 5 +- .../TraceView/TraceViewContainer.test.tsx | 4 +- .../explore/TraceView/TraceViewContainer.tsx | 3 +- .../Actions/TracePageActions.tsx | 8 +- .../SearchBar/NextPrevResult.tsx | 9 +- .../SpanFilters/SpanFilters.tsx | 32 +- .../SpanFilters/SpanFiltersTags.tsx | 23 +- .../SpanGraph/ViewingLayer.tsx | 3 +- .../SpanDetail/AccordianReferences.tsx | 5 +- .../SpanDetail/SpanFlameGraph.tsx | 5 +- .../SpanDetail/index.test.tsx | 4 +- .../TraceTimelineViewer/SpanDetail/index.tsx | 13 +- .../TimelineCollapser.test.tsx | 4 +- .../TimelineHeaderRow/TimelineCollapser.tsx | 21 +- .../TimelineHeaderRow.test.tsx | 4 +- .../VirtualizedTraceView.tsx | 3 +- .../TraceTimelineViewer/index.test.tsx | 4 +- .../components/common/SearchBarInput.tsx | 13 +- .../components/settings/SpanBarSettings.tsx | 16 +- .../demo/DraggableManagerDemo.tsx | 20 +- .../explore/TraceView/createSpanLink.tsx | 28 +- .../extensions/ConfirmNavigationModal.tsx | 7 +- .../extensions/toolbar/BasicExtensions.tsx | 8 +- .../toolbar/QuerylessAppsExtensions.tsx | 3 +- .../features/explore/spec/helper/setup.tsx | 3 +- public/locales/en-US/grafana.json | 299 +++++++++++++++++ 62 files changed, 883 insertions(+), 446 deletions(-) diff --git a/.betterer.results b/.betterer.results index 053ef6bfd02..b93947f59dd 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2521,74 +2521,33 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "public/app/features/api-keys/ApiKeysActionBar.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/api-keys/ApiKeysPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] - ], - "public/app/features/api-keys/ApiKeysTable.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] - ], - "public/app/features/api-keys/MigrateToServiceAccountsCard.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], [0, 0, 0, "No untranslated strings. Wrap text with ", "7"] ], - "public/app/features/auth-config/AuthDrawer.tsx:5381": [ + "public/app/features/api-keys/MigrateToServiceAccountsCard.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] - ], - "public/app/features/auth-config/AuthProvidersListPage.tsx:5381": [ - [0, 0, 0, "\'@grafana/data/src/types/config\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], - "public/app/features/auth-config/ProviderConfigForm.tsx:5381": [ + "public/app/features/auth-config/AuthDrawer.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"] - ], - "public/app/features/auth-config/components/ConfigureAuthCTA.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], - "public/app/features/auth-config/components/ServerDiscoveryModal.tsx:5381": [ + "public/app/features/auth-config/AuthProvidersListPage.tsx:5381": [ + [0, 0, 0, "\'@grafana/data/src/types/config\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + ], + "public/app/features/auth-config/ProviderConfigForm.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/auth-config/fields.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -2631,9 +2590,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/connections/components/ConnectionsRedirectNotice/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] @@ -2661,16 +2618,13 @@ exports[`better eslint`] = { "public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/connections/tabs/ConnectData/NoAccessModal/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], "public/app/features/connections/tabs/ConnectData/Search/Search.tsx:5381": [ - [0, 0, 0, "Do not use the t() function outside of a component or function", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] + [0, 0, 0, "Do not use the t() function outside of a component or function", "0"] ], "public/app/features/connections/tabs/ConnectData/Search/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] @@ -3926,98 +3880,41 @@ exports[`better eslint`] = { "public/app/features/dimensions/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "public/app/features/explore/CorrelationEditorModeBar.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/features/explore/CorrelationHelper.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/explore/CorrelationTransformationAddModal.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"] - ], - "public/app/features/explore/CorrelationUnsavedChangesModal.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], - "public/app/features/explore/Explore.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"] + "public/app/features/explore/CorrelationUnsavedChangesModal.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/explore/ExploreRunQueryButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], - "public/app/features/explore/ExploreToolbar.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/explore/FeatureTogglePage.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/explore/LiveTailButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] - ], "public/app/features/explore/Logs/LiveLogs.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/Logs.test.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/organize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], "public/app/features/explore/Logs/Logs.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/features/explore/Logs/LogsColumnSearch.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "public/app/features/explore/Logs/LogsContainer.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/explore/Logs/LogsFeedback.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4025,19 +3922,8 @@ exports[`better eslint`] = { "public/app/features/explore/Logs/LogsMetaRow.test.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/organize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], - "public/app/features/explore/Logs/LogsMetaRow.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] - ], "public/app/features/explore/Logs/LogsSamplePanel.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/LogsTable.test.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/organize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] @@ -4045,43 +3931,24 @@ exports[`better eslint`] = { "public/app/features/explore/Logs/LogsTableAvailableFields.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], - "public/app/features/explore/Logs/LogsTableEmptyFields.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/explore/Logs/LogsTableMultiSelect.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/LogsTableNavField.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/LogsTableWrap.test.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/transformations/transformers/organize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], "public/app/features/explore/Logs/LogsTableWrap.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/explore/Logs/LogsVolumePanelList.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/MetaInfoText.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/explore/NoDataSourceCallToAction.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] - ], "public/app/features/explore/NodeGraph/NodeGraphContainer.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4092,8 +3959,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/explore/PrometheusListView/RawListItem.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/PrometheusListView/RawListItemAttributes.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4113,21 +3979,12 @@ exports[`better eslint`] = { "public/app/features/explore/ShortLinkButtonMenu.tsx:5381": [ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"] ], - "public/app/features/explore/SupplementaryResultError.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/explore/TraceView/TraceView.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] - ], - "public/app/features/explore/TraceView/TraceViewContainer.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] + [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/explore/TraceView/components/TracePageHeader/Actions/TracePageActions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], "public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -4135,10 +3992,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/TracePageSearchBar.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -4154,35 +4008,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "13"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "14"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "15"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "16"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "17"] - ], - "public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFiltersTags.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"] - ], - "public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/ViewingLayer.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/explore/TraceView/components/TracePageHeader/TracePageHeader.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], @@ -4206,43 +4032,20 @@ exports[`better eslint`] = { "public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianText.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], - "public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"] - ], "public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineHeaderRow.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./TimelineHeaderRow\`)", "0"] ], - "public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/explore/TraceView/components/TraceTimelineViewer/utils.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`../utils/date\`)", "0"] ], - "public/app/features/explore/TraceView/components/common/SearchBarInput.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] - ], "public/app/features/explore/TraceView/components/demo/trace-generators.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -4280,13 +4083,7 @@ exports[`better eslint`] = { "public/app/features/explore/TraceView/components/settings/SpanBarSettings.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"] + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"] ], "public/app/features/explore/TraceView/components/types/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`../settings/SpanBarSettings\`)", "0"], @@ -4298,11 +4095,7 @@ exports[`better eslint`] = { "public/app/features/explore/TraceView/components/utils/DraggableManager/demo/DraggableManagerDemo.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/explore/TraceView/components/utils/DraggableManager/demo/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./DraggableManagerDemo\`)", "0"] @@ -4314,31 +4107,18 @@ exports[`better eslint`] = { ], "public/app/features/explore/TraceView/createSpanLink.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"] + [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/explore/extensions/ConfirmNavigationModal.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] - ], - "public/app/features/explore/extensions/toolbar/BasicExtensions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], - "public/app/features/explore/extensions/toolbar/QuerylessAppsExtensions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/hooks/useStateSync/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./external.utils\`)", "0"] ], "public/app/features/explore/spec/helper/setup.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/features/explore/state/main.test.ts:5381": [ [0, 0, 0, "\'@grafana/data/src/utils/url\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] diff --git a/public/app/features/api-keys/ApiKeysActionBar.tsx b/public/app/features/api-keys/ApiKeysActionBar.tsx index e5edff671ac..ffae1b7cbc7 100644 --- a/public/app/features/api-keys/ApiKeysActionBar.tsx +++ b/public/app/features/api-keys/ApiKeysActionBar.tsx @@ -1,4 +1,5 @@ import { FilterInput, InlineField } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; interface Props { searchQuery: string; @@ -10,7 +11,11 @@ export const ApiKeysActionBar = ({ searchQuery, disabled, onSearchChange }: Prop return (
- +
); diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index 1f3e769e86b..28cc53bca23 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -6,7 +6,7 @@ import { connect, ConnectedProps } from 'react-redux'; import { InlineField, InlineSwitch, Modal, Button, EmptyState } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { contextSrv } from 'app/core/core'; -import { t } from 'app/core/internationalization'; +import { t, Trans } from 'app/core/internationalization'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { AccessControlAction, ApiKey, ApikeyMigrationResult, StoreState } from 'app/types'; @@ -127,7 +127,10 @@ export class ApiKeysPageUnconnected extends PureComponent { onSearchChange={this.onSearchQueryChange} /> ) : null} - + {apiKeys.length > 0 ? ( @@ -175,10 +178,17 @@ const styles: { [key: string]: React.CSSProperties } = { export const MigrationSummary: React.FC = ({ visible, data, onDismiss }) => { return ( - + {data.failedApikeyIDs.length === 0 && (
-

Migration Successful!

+

+ Migration Successful! +

Total: {data.total} @@ -220,7 +230,7 @@ export const MigrationSummary: React.FC = ({ visible, dat )} diff --git a/public/app/features/api-keys/ApiKeysTable.tsx b/public/app/features/api-keys/ApiKeysTable.tsx index f695fe5d4e1..983903d9c15 100644 --- a/public/app/features/api-keys/ApiKeysTable.tsx +++ b/public/app/features/api-keys/ApiKeysTable.tsx @@ -3,6 +3,7 @@ import { css } from '@emotion/css'; import { dateTimeFormat, GrafanaTheme2, TimeZone } from '@grafana/data'; import { Button, DeleteButton, Icon, Stack, Tooltip, useTheme2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; +import { Trans, t } from 'app/core/internationalization'; import { AccessControlAction } from 'app/types'; import { ApiKey } from '../../types'; @@ -22,10 +23,18 @@ export const ApiKeysTable = ({ apiKeys, timeZone, onDelete, onMigrate }: Props) - - - - + + + + @@ -51,10 +60,12 @@ export const ApiKeysTable = ({ apiKeys, timeZone, onDelete, onMigrate }: Props)
NameRoleExpiresLast used at + Name + + Role + + Expires + + Last used at +
onDelete(key)} disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.ActionAPIKeysDelete, key)} diff --git a/public/app/features/api-keys/MigrateToServiceAccountsCard.tsx b/public/app/features/api-keys/MigrateToServiceAccountsCard.tsx index 87ff5e72ed4..aa9f20fa292 100644 --- a/public/app/features/api-keys/MigrateToServiceAccountsCard.tsx +++ b/public/app/features/api-keys/MigrateToServiceAccountsCard.tsx @@ -3,6 +3,7 @@ import { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, ConfirmModal, useStyles2 } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; interface Props { onMigrate: () => void; @@ -21,15 +22,29 @@ export const MigrateToServiceAccountsCard = ({ onMigrate, apikeysCount, disabled target="_blank" rel="noopener noreferrer" > - Find out more about the migration here. + + Find out more about the migration here. + ); - const migrationBoxDesc = Migrating all API keys will hide the API keys tab.; + const migrationBoxDesc = ( + + + Migrating all API keys will hide the API keys tab. + + + ); return ( <> {apikeysCount > 0 && ( - +
API keys are deprecated and will be removed from Grafana on Jan 31, 2025. Each API key will be migrated into a service account with a token and will continue to work as they were. We encourage you to migrate your API @@ -37,7 +52,9 @@ export const MigrateToServiceAccountsCard = ({ onMigrate, apikeysCount, disabled
- +
No API keys were found. If you reload the browser, this page will not be available anymore.
diff --git a/public/app/features/auth-config/AuthDrawer.tsx b/public/app/features/auth-config/AuthDrawer.tsx index a9530e92d9f..b98e59855dd 100644 --- a/public/app/features/auth-config/AuthDrawer.tsx +++ b/public/app/features/auth-config/AuthDrawer.tsx @@ -5,6 +5,7 @@ import { connect, ConnectedProps } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Drawer, Text, TextLink, Switch, useStyles2 } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; +import { t, Trans } from 'app/core/internationalization'; import { StoreState } from 'app/types'; import { loadSettings, saveSettings } from './state/actions'; @@ -84,10 +85,21 @@ export const AuthDrawerUnconnected = ({ const styles = useStyles2(getStyles); return ( - +
- Advanced Auth - Enable insecure email lookup + + Advanced Auth + + + + Enable insecure email lookup + + Allow users to use the same email address to log into Grafana with different identity providers. @@ -100,7 +112,7 @@ export const AuthDrawerUnconnected = ({ onClick={resetButtonOnClick} tooltip="This action will disregard any saved changes and load the configuration from the configuration file." > - Reset + Reset ); diff --git a/public/app/features/auth-config/AuthProvidersListPage.tsx b/public/app/features/auth-config/AuthProvidersListPage.tsx index 717906f0f3a..46909ed9397 100644 --- a/public/app/features/auth-config/AuthProvidersListPage.tsx +++ b/public/app/features/auth-config/AuthProvidersListPage.tsx @@ -6,6 +6,7 @@ import { reportInteraction } from '@grafana/runtime'; import { Grid, TextLink, ToolbarButton } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { config } from 'app/core/config'; +import { Trans } from 'app/core/internationalization'; import { StoreState } from 'app/types'; import AuthDrawer from './AuthDrawer'; @@ -95,7 +96,7 @@ export const AuthConfigPageUnconnected = ({ actions={ config.buildInfo.edition !== GrafanaEdition.OpenSource && ( setShowDrawer(true)}> - Auth settings + Auth settings ) } diff --git a/public/app/features/auth-config/ProviderConfigForm.tsx b/public/app/features/auth-config/ProviderConfigForm.tsx index 0fb3567a16b..cc6372500b8 100644 --- a/public/app/features/auth-config/ProviderConfigForm.tsx +++ b/public/app/features/auth-config/ProviderConfigForm.tsx @@ -16,6 +16,7 @@ import { Stack, Switch, } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { FormPrompt } from '../../core/components/FormPrompt/FormPrompt'; import { Page } from '../../core/components/Page/Page'; @@ -54,7 +55,10 @@ export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConf const additionalActionsMenu = ( { setResetConfig(true); @@ -163,7 +167,7 @@ export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConf reset(); }} /> -
diff --git a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx index 2fc70dea167..3eec720cc67 100644 --- a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx +++ b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, Modal, Icon, Button } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { type CardGridItem } from '../CardGrid'; @@ -92,11 +93,17 @@ export function NoAccessModal({ item, isOpen, onDismiss }: NoAccessModalProps) { Editors cannot add new connections. You may check to see if it is already configured in{' '} Data sources.

-

To add a new connection, contact your Grafana admin.

+

+ + To add a new connection, contact your Grafana admin. + +

- +
diff --git a/public/app/features/connections/tabs/ConnectData/Search/Search.tsx b/public/app/features/connections/tabs/ConnectData/Search/Search.tsx index ce5f25a538f..448abbc70dc 100644 --- a/public/app/features/connections/tabs/ConnectData/Search/Search.tsx +++ b/public/app/features/connections/tabs/ConnectData/Search/Search.tsx @@ -37,7 +37,7 @@ export const Search = ({ onChange, value }: Props) => { onChange={onChange} prefix={} placeholder={placeholder} - aria-label="Search all" + aria-label={t('connections.search.aria-label-search-all', 'Search all')} /> ); diff --git a/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx b/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx index 1610ff0152d..ad39f27c077 100644 --- a/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { IconName, isIconName, GrafanaTheme2 } from '@grafana/data'; import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui'; import { TooltipPlacement } from '@grafana/ui/internal'; +import { t } from 'app/core/internationalization'; type CommonProps = { contentOutlineExpanded?: boolean; @@ -64,7 +65,10 @@ export function ContentOutlineItemButton({
diff --git a/public/app/features/explore/CorrelationHelper.tsx b/public/app/features/explore/CorrelationHelper.tsx index 14bcd722857..b71aa6b3e18 100644 --- a/public/app/features/explore/CorrelationHelper.tsx +++ b/public/app/features/explore/CorrelationHelper.tsx @@ -18,6 +18,7 @@ import { Icon, Stack, } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { useDispatch, useSelector } from 'app/types'; import { getTransformationVars } from '../correlations/transformations'; @@ -156,7 +157,7 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => { } /> )} - + The correlation link will appear by the {correlations.resultField} field. You can use the following variables to set up your correlations:
@@ -179,7 +180,7 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
             
           }
         >
-          
+          
              {
               }}
             />
           
-          
+          
             
           
         
@@ -217,7 +218,7 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
             }}
             className={styles.transformationAction}
           >
-            Add transformation
+            Add transformation
           
           {transformations.map((transformation, i) => {
             const { type, field, expression, mapValue } = transformation;
@@ -241,14 +242,17 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
                    {
                       setTransformationIdxToEdit(i);
                       setShowTransformationAddModal(true);
                     }}
                   />
                    setTransformations(transformations.filter((_, idx) => i !== idx))}
                     closeOnConfirm
                   />
diff --git a/public/app/features/explore/CorrelationTransformationAddModal.tsx b/public/app/features/explore/CorrelationTransformationAddModal.tsx
index fe555e26274..ab70ddb9743 100644
--- a/public/app/features/explore/CorrelationTransformationAddModal.tsx
+++ b/public/app/features/explore/CorrelationTransformationAddModal.tsx
@@ -5,6 +5,7 @@ import { useForm, Controller } from 'react-hook-form';
 
 import { DataLinkTransformationConfig, ScopedVars } from '@grafana/data';
 import { Button, Field, Icon, Input, Label, Modal, Select, Tooltip, Stack } from '@grafana/ui';
+import { t, Trans } from 'app/core/internationalization';
 
 import {
   getSupportedTransTypeDetails,
@@ -137,7 +138,7 @@ export const CorrelationTransformationAddModal = ({
         A transformation extracts variables out of a single field. These variables will be available along with your
         field variables.
       

- + ( @@ -152,7 +153,7 @@ export const CorrelationTransformationAddModal = ({ options={Object.entries(fieldList).map((entry) => { return { label: entry[0], value: entry[0] }; })} - aria-label="field" + aria-label={t('explore.correlation-transformation-add-modal.aria-label-field', 'Field')} /> )} name={`field` as const} @@ -168,7 +169,7 @@ export const CorrelationTransformationAddModal = ({ autoEscape={false} />
- + ( @@ -183,7 +184,7 @@ export const CorrelationTransformationAddModal = ({ }); }} options={getTransformOptions()} - aria-label="type" + aria-label={t('explore.correlation-transformation-add-modal.aria-label-type', 'Type')} /> )} name={`type` as const} @@ -193,7 +194,10 @@ export const CorrelationTransformationAddModal = ({ + ) : ( 'Expression' ) @@ -208,9 +212,12 @@ export const CorrelationTransformationAddModal = ({ + ) : ( - 'Variable Name' + 'Variable name' ) } htmlFor={`${id}-mapValue`} @@ -232,7 +239,7 @@ export const CorrelationTransformationAddModal = ({ )} diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 206b006c66b..f1148f3c735 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -31,6 +31,7 @@ import { } from '@grafana/ui'; import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR } from '@grafana/ui/internal'; import { supportedFeatures } from 'app/core/history/richHistoryStorageProvider'; +import { t } from 'app/core/internationalization'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { StoreState } from 'app/types'; @@ -371,7 +372,7 @@ export class Explore extends PureComponent { const { graphResult, timeZone, queryResponse, showFlameGraph } = this.props; return ( - + { renderTablePanel(width: number) { const { exploreId, timeZone } = this.props; return ( - + { renderRawPrometheus(width: number) { const { exploreId, datasourceInstance, timeZone } = this.props; return ( - + { gap: theme.spacing(1), }); return ( - + { const { logsSample, timeZone, setSupplementaryQueryEnabled, exploreId, datasourceInstance, queries } = this.props; return ( - + { const datasourceType = datasourceInstance ? datasourceInstance?.type : 'unknown'; return ( - + { renderFlameGraphPanel() { const { queryResponse } = this.props; return ( - + ); @@ -508,7 +530,7 @@ export class Explore extends PureComponent { return ( // If there is no data (like 404) we show a separate error so no need to show anything here dataFrames.length && ( - + {
{datasourceInstance ? ( <> - + {correlationsBox} diff --git a/public/app/features/explore/ExploreRunQueryButton.tsx b/public/app/features/explore/ExploreRunQueryButton.tsx index 48c7831c9d2..20f6fa3272b 100644 --- a/public/app/features/explore/ExploreRunQueryButton.tsx +++ b/public/app/features/explore/ExploreRunQueryButton.tsx @@ -118,7 +118,14 @@ export function ExploreRunQueryButton({ return ( setOpenRunQueryButton(state)} placement="bottom-start" overlay={menu}> - + {t('explore.run-query.run-query-button', 'Run query')} diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 85166280a89..afc59393b6c 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -227,7 +227,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle - Outline + Outline , Pause the live stream : <>Start live stream your logs} + content={ + isLive && !isPaused ? ( + <> + Pause the live stream + + ) : ( + <> + Start live stream your logs + + ) + } placement="bottom" > - Stop and exit the live stream} placement="bottom"> + + + Stop and exit the live stream + + + } + placement="bottom" + > diff --git a/public/app/features/explore/Logs/LiveLogs.tsx b/public/app/features/explore/Logs/LiveLogs.tsx index 07c33ce6a0c..596d956887a 100644 --- a/public/app/features/explore/Logs/LiveLogs.tsx +++ b/public/app/features/explore/Logs/LiveLogs.tsx @@ -6,6 +6,7 @@ import tinycolor from 'tinycolor2'; import { LogRowModel, dateTimeFormat, GrafanaTheme2, LogsSortOrder } from '@grafana/data'; import { TimeZone } from '@grafana/schema'; import { Button, Themeable2, withTheme2 } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { LogMessageAnsi } from '../../logs/components/LogMessageAnsi'; import { getLogRowStyles } from '../../logs/components/getLogRowStyles'; @@ -172,10 +173,10 @@ class LiveLogs extends PureComponent { {isPaused ? 'Resume' : 'Pause'} {isPaused || (this.rowsToRender().length > 0 && ( diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index 5bf10d2dac2..1934a768a6e 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -49,7 +49,7 @@ import { withTheme2, } from '@grafana/ui'; import { mapMouseEventToMode } from '@grafana/ui/internal'; -import { Trans } from 'app/core/internationalization'; +import { Trans, t } from 'app/core/internationalization'; import store from 'app/core/store'; import { createAndCopyShortLink, getLogsPermalinkRange } from 'app/core/utils/shortLinks'; import { InfiniteScroll } from 'app/features/logs/components/InfiniteScroll'; @@ -804,7 +804,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { /> )} = (props: Props) => { titleItems={[ config.featureToggles.logsExploreTableVisualisation ? ( visualisationType === 'logs' ? null : ( - + ) @@ -868,7 +868,11 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { {visualisationType !== 'table' && (
- + = (props: Props) => { id={`show-time_${exploreId}`} /> - + = (props: Props) => { id={`unique-labels_${exploreId}`} /> - + = (props: Props) => { id={`wrap-lines_${exploreId}`} /> - + = (props: Props) => { id={`prettify_${exploreId}`} /> - + ({ label: capitalize(dedupType), @@ -920,7 +940,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => {
- + {(controls) => ( - Show original line + Show original line ), } @@ -165,8 +166,11 @@ export const LogsMetaRow = memo( } const downloadMenu = ( + {/* eslint-disable-next-line @grafana/no-untranslated-strings */} downloadLogs(DownloadFormat.Text)} /> + {/* eslint-disable-next-line @grafana/no-untranslated-strings */} downloadLogs(DownloadFormat.Json)} /> + {/* eslint-disable-next-line @grafana/no-untranslated-strings */} downloadLogs(DownloadFormat.CSV)} /> ); @@ -185,7 +189,7 @@ export const LogsMetaRow = memo( {!config.exploreHideLogsDownload && ( - Download + Download )} diff --git a/public/app/features/explore/Logs/LogsSamplePanel.tsx b/public/app/features/explore/Logs/LogsSamplePanel.tsx index 63ec5219b06..4af65d19ef8 100644 --- a/public/app/features/explore/Logs/LogsSamplePanel.tsx +++ b/public/app/features/explore/Logs/LogsSamplePanel.tsx @@ -13,6 +13,7 @@ import { import { reportInteraction } from '@grafana/runtime'; import { DataQuery, TimeZone } from '@grafana/schema'; import { Button, Collapse, Icon, Tooltip, useStyles2 } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import store from 'app/core/store'; import { LogRows } from '../../logs/components/LogRows'; @@ -69,7 +70,9 @@ export function LogsSamplePanel(props: Props) { return ( ); }; @@ -80,12 +83,23 @@ export function LogsSamplePanel(props: Props) { LogsSamplePanelContent = null; } else if (queryResponse.error !== undefined) { LogsSamplePanelContent = ( - + ); } else if (queryResponse.state === LoadingState.Loading) { - LogsSamplePanelContent = Logs sample is loading...; + LogsSamplePanelContent = ( + + Logs sample is loading... + + ); } else if (queryResponse.data.length === 0 || queryResponse.data.every((frame) => frame.length === 0)) { - LogsSamplePanelContent = No logs sample data.; + LogsSamplePanelContent = ( + + No logs sample data. + + ); } else { const logs = dataFrameToLogsModel(queryResponse.data); LogsSamplePanelContent = ( diff --git a/public/app/features/explore/Logs/LogsTableEmptyFields.tsx b/public/app/features/explore/Logs/LogsTableEmptyFields.tsx index 1704050a461..0b68ae8fef7 100644 --- a/public/app/features/explore/Logs/LogsTableEmptyFields.tsx +++ b/public/app/features/explore/Logs/LogsTableEmptyFields.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useTheme2 } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; function getStyles(theme: GrafanaTheme2) { return { @@ -16,5 +17,9 @@ function getStyles(theme: GrafanaTheme2) { export function LogsTableEmptyFields() { const theme = useTheme2(); const styles = getStyles(theme); - return
No fields
; + return ( +
+ No fields +
+ ); } diff --git a/public/app/features/explore/Logs/LogsTableMultiSelect.tsx b/public/app/features/explore/Logs/LogsTableMultiSelect.tsx index 10308e761a7..ad0b2dec903 100644 --- a/public/app/features/explore/Logs/LogsTableMultiSelect.tsx +++ b/public/app/features/explore/Logs/LogsTableMultiSelect.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data/src'; import { useTheme2 } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { LogsTableActiveFields } from './LogsTableActiveFields'; import { LogsTableAvailableFields } from './LogsTableAvailableFields'; @@ -71,7 +72,9 @@ export const LogsTableMultiSelect = (props: { id={'selected-fields'} /> -
Fields
+
+ Fields +
{props.draggable && ( 1 && (
setSpanFiltersSearch({ ...search, serviceNameOperator: v.value! })} options={[toOption('='), toOption('!=')]} value={search.serviceNameOperator} /> setSpanFiltersSearch({ ...search, spanNameOperator: v.value! })} options={[toOption('='), toOption('!=')]} value={search.spanNameOperator} /> setSpanFiltersSearch({ ...search, fromOperator: v.value! })} options={[toOption('>'), toOption('>=')]} value={search.fromOperator} @@ -212,7 +216,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => { />
onTagChange(tag, v)} onOpenMenu={getTagKeys} options={tagKeys || (tag.key ? [tag.key].map(toOption) : [])} - placeholder="Select tag" + placeholder={t('explore.span-filters-tags.placeholder-select-tag', 'Select tag')} value={tag.key || null} />
{ @@ -143,13 +144,13 @@ export const SpanFiltersTags = ({ search, trace, setSearch, tagKeys, setTagKeys, }); }} options={tagValues[tag.id] ? tagValues[tag.id] : tag.value ? [tag.value].map(toOption) : []} - placeholder="Select value" + placeholder={t('explore.span-filters-tags.placeholder-select-value', 'Select value')} value={tag.value} /> )} {(tag.operator === '=~' || tag.operator === '!~') && ( { setSearch({ ...search, @@ -158,7 +159,7 @@ export const SpanFiltersTags = ({ search, trace, setSearch, tagKeys, setTagKeys, }), }); }} - placeholder="Tag value" + placeholder={t('explore.span-filters-tags.placeholder-tag-value', 'Tag value')} width={18} value={tag.value || ''} /> @@ -166,21 +167,21 @@ export const SpanFiltersTags = ({ search, trace, setSearch, tagKeys, setTagKeys, {(tag.key || tag.value || search.tags.length > 1) && ( removeTag(tag.id)} - tooltip="Remove tag" + tooltip={t('explore.span-filters-tags.tooltip-remove-tag', 'Remove tag')} /> )} {(tag.key || tag.value) && i === search.tags.length - 1 && ( )} diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/ViewingLayer.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/ViewingLayer.tsx index e899b3faab9..f3d878d981b 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/ViewingLayer.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/ViewingLayer.tsx @@ -18,6 +18,7 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { withTheme2, stylesFactory, Button } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { autoColor } from '../../Theme'; import { TUpdateViewRangeTimeFunction, ViewRange, ViewRangeTimeUpdate, TNil } from '../../index'; @@ -354,7 +355,7 @@ export class UnthemedViewingLayer extends React.PureComponent - Reset Selection + Reset selection )} {arrow} - References + + References + {' '} ({data.length}) diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx index ccf70c02b05..5694657462a 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx @@ -16,6 +16,7 @@ import { FlameGraph } from '@grafana/flamegraph'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { config, DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; import { useStyles2 } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { Query } from 'app/plugins/datasource/grafana-pyroscope-datasource/types'; @@ -174,7 +175,9 @@ export default function SpanFlameGraph(props: SpanFlameGraphProps) { return (
-
Flame graph
+
+ Flame graph +
config.theme2} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx index 1ad71c04cbd..b326a7aee79 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx @@ -216,13 +216,13 @@ describe('', () => { it('renders the span tags', async () => { render(); - await userEvent.click(screen.getByRole('switch', { name: /Span Attributes/ })); + await userEvent.click(screen.getByRole('switch', { name: /Span attributes/ })); expect(props.tagsToggle).toHaveBeenLastCalledWith(span.spanID); }); it('renders the process tags', async () => { render(); - await userEvent.click(screen.getByRole('switch', { name: /Resource Attributes/ })); + await userEvent.click(screen.getByRole('switch', { name: /Resource attributes/ })); expect(props.processToggle).toHaveBeenLastCalledWith(span.spanID); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx index 7a6ec6dbc17..239fa28b844 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx @@ -29,6 +29,7 @@ import { import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { TimeZone } from '@grafana/schema'; import { Divider, Icon, TextArea, useStyles2 } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { pyroscopeProfileIdTagKey } from '../../../createSpanLink'; import { autoColor } from '../../Theme'; @@ -321,7 +322,7 @@ export default function SpanDetail(props: SpanDetailProps) {
tagsToggle(spanID)} @@ -330,7 +331,7 @@ export default function SpanDetail(props: SpanDetailProps) { processToggle(spanID)} @@ -352,7 +353,11 @@ export default function SpanDetail(props: SpanDetailProps) { Warnings} + label={ + + Warnings + + } data={warnings} isOpen={isWarningsOpen} onToggle={() => warningsToggle(spanID)} @@ -360,7 +365,7 @@ export default function SpanDetail(props: SpanDetailProps) { )} {stackTraces?.length ? ( { diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.test.tsx index 19cde0e0e96..251c2923b26 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.test.tsx @@ -35,8 +35,8 @@ describe('TimelineCollapser test', () => { setup(); expect(screen.getByTestId('TimelineCollapser')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Expand All' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Collapse All' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Expand all' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Collapse all' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Expand +1' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Collapse +1' })).toBeInTheDocument(); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx index 457c54f1feb..9811f618b36 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx @@ -15,6 +15,7 @@ import { css } from '@emotion/css'; import { IconButton, useStyles2 } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; const getStyles = () => ({ TimelineCollapser: css({ @@ -38,17 +39,29 @@ export function TimelineCollapser(props: CollapserProps) { const styles = useStyles2(getStyles); return (
- - + + { it('renders the collapser controls', () => { setup(); - expect(screen.getByRole('button', { name: 'Expand All' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Collapse All' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Expand all' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Collapse all' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Expand +1' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Collapse +1' })).toBeInTheDocument(); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx index d84d9e984c5..6a8127b8cb1 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -23,6 +23,7 @@ import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { config, reportInteraction } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; import { stylesFactory, withTheme2, ToolbarButton } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { PEER_SERVICE } from '../constants/tag-keys'; import { CriticalPathSection, SpanBarOptions, SpanLinkFunc, TNil } from '../types'; @@ -655,7 +656,7 @@ export class UnthemedVirtualizedTraceView extends React.Component )} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.test.tsx index 7ac1261dc05..b95d544dd11 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.test.tsx @@ -67,8 +67,8 @@ describe('', () => { const expandOne = screen.getByRole('button', { name: 'Expand +1' }); const collapseOne = screen.getByRole('button', { name: 'Collapse +1' }); - const expandAll = screen.getByRole('button', { name: 'Expand All' }); - const collapseAll = screen.getByRole('button', { name: 'Collapse All' }); + const expandAll = screen.getByRole('button', { name: 'Expand all' }); + const collapseAll = screen.getByRole('button', { name: 'Collapse all' }); expect(expandOne).toBeInTheDocument(); expect(collapseOne).toBeInTheDocument(); diff --git a/public/app/features/explore/TraceView/components/common/SearchBarInput.tsx b/public/app/features/explore/TraceView/components/common/SearchBarInput.tsx index 664e1e6e2b3..3b4c7bae65a 100644 --- a/public/app/features/explore/TraceView/components/common/SearchBarInput.tsx +++ b/public/app/features/explore/TraceView/components/common/SearchBarInput.tsx @@ -15,6 +15,7 @@ import * as React from 'react'; import { IconButton, Input } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; type Props = { value: string | undefined; @@ -34,13 +35,21 @@ export default class SearchBarInput extends React.PureComponent { const { value } = this.props; const suffix = ( - <>{value && value.length && } + <> + {value && value.length && ( + + )} + ); return (
this.props.onChange(e.currentTarget.value)} suffix={suffix} value={value} diff --git a/public/app/features/explore/TraceView/components/settings/SpanBarSettings.tsx b/public/app/features/explore/TraceView/components/settings/SpanBarSettings.tsx index 11710cef528..0a5df0cec61 100644 --- a/public/app/features/explore/TraceView/components/settings/SpanBarSettings.tsx +++ b/public/app/features/explore/TraceView/components/settings/SpanBarSettings.tsx @@ -9,6 +9,7 @@ import { } from '@grafana/data'; import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/plugin-ui'; import { InlineField, InlineFieldRow, Input, Select, useStyles2 } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; export interface SpanBarOptions { type?: string; @@ -32,7 +33,12 @@ export default function SpanBarSettings({ options, onOptionsChange }: Props) { return (
- + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'spanBar', { ...options.jsonData.spanBar, @@ -82,7 +88,7 @@ export const SpanBarSection = ({ options, onOptionsChange }: DataSourcePluginOpt return ( -

DraggableManager demo

+

+ DraggableManager demo +

-

Dragging a Divider

+

+ Dragging a divider +

Click and drag the gray divider in the colored area, below.

Value: {dividerPosition.toFixed(3)}

@@ -57,8 +63,14 @@ export default class DraggableManagerDemo extends PureComponent<{}, DraggableMan
-

Dragging a Sub-Region

-

Click and drag horizontally somewhere in the colored area, below.

+

+ Dragging a sub-region +

+

+ + Click and drag horizontally somewhere in the colored area, below. + +

Value: {regionDragging && regionDragging.map((n) => n.toFixed(3)).join(', ')}

diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx index 662876c3b21..098e7292bb0 100644 --- a/public/app/features/explore/TraceView/createSpanLink.tsx +++ b/public/app/features/explore/TraceView/createSpanLink.tsx @@ -22,6 +22,7 @@ import { PromQuery } from '@grafana/prometheus'; import { getTemplateSrv } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; import { Icon } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { LokiQuery } from '../../../plugins/datasource/loki/types'; @@ -247,7 +248,15 @@ function legacyCreateSpanLinkFactory( href: link.href, title: 'Related logs', onClick: link.onClick, - content: , + content: ( + + ), field, type: SpanLinkType.Logs, }); @@ -308,7 +317,15 @@ function legacyCreateSpanLinkFactory( title: query?.name, href: link.href, onClick: link.onClick, - content: , + content: ( + + ), field, type: SpanLinkType.Metrics, }); @@ -359,7 +376,12 @@ function legacyCreateSpanLinkFactory( links.push({ title: 'Session for this span', href: feO11yLink, - content: , + content: ( + + ), field, type: SpanLinkType.Session, }); diff --git a/public/app/features/explore/extensions/ConfirmNavigationModal.tsx b/public/app/features/explore/extensions/ConfirmNavigationModal.tsx index 1d5c35302ca..8f93a1336fa 100644 --- a/public/app/features/explore/extensions/ConfirmNavigationModal.tsx +++ b/public/app/features/explore/extensions/ConfirmNavigationModal.tsx @@ -3,6 +3,7 @@ import { ReactElement } from 'react'; import { locationUtil } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import { Button, Modal, Stack } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; type Props = { onDismiss: () => void; @@ -25,13 +26,13 @@ export function ConfirmNavigationModal(props: Props): ReactElement { diff --git a/public/app/features/explore/extensions/toolbar/BasicExtensions.tsx b/public/app/features/explore/extensions/toolbar/BasicExtensions.tsx index 5332ac6bfd6..9b3fab23ed4 100644 --- a/public/app/features/explore/extensions/toolbar/BasicExtensions.tsx +++ b/public/app/features/explore/extensions/toolbar/BasicExtensions.tsx @@ -1,6 +1,7 @@ import { lazy, Suspense } from 'react'; import { Dropdown, ToolbarButton } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction } from 'app/types/accessControl'; @@ -38,7 +39,12 @@ export function BasicExtensions(props: ExtensionDropdownProps) { return ( <> - + Add diff --git a/public/app/features/explore/extensions/toolbar/QuerylessAppsExtensions.tsx b/public/app/features/explore/extensions/toolbar/QuerylessAppsExtensions.tsx index 7433c74393c..23e566358b7 100644 --- a/public/app/features/explore/extensions/toolbar/QuerylessAppsExtensions.tsx +++ b/public/app/features/explore/extensions/toolbar/QuerylessAppsExtensions.tsx @@ -1,6 +1,7 @@ import { first } from 'lodash'; import { Dropdown, ToolbarButton } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { Trans } from '../../../../core/internationalization'; import { ToolbarExtensionPointMenu } from '../ToolbarExtensionPointMenu'; @@ -29,7 +30,7 @@ export function QuerylessAppsExtensions(props: ExtensionDropdownProps) { <> { props.onChange({ ...props.query, expr: event.target.value }); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 1d1e1740791..774dba60d03 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -878,8 +878,38 @@ } }, "api-keys": { + "api-keys-action-bar": { + "placeholder-search-keys": "Search keys" + }, + "api-keys-page-unconnected": { + "label-include-expired-keys": "Include expired keys" + }, + "api-keys-table": { + "aria-label-delete-api-key": "Delete API key", + "expires": "Expires", + "last-used-at": "Last used at", + "migrate-to-service-account": "Migrate to service account", + "name": "Name", + "role": "Role" + }, "empty-state": { "message": "No API keys found" + }, + "migrate-to-service-accounts-card": { + "docs-link": { + "about-migration": "Find out more about the migration here." + }, + "migrate-all-service-accounts": "Migrate all service accounts", + "migration-box-desc": { + "migrating": "Migrating all API keys will hide the API keys tab." + }, + "title-no-api-keys-found": "No API keys found", + "title-switch-service-accounts": "Switch from API keys to service accounts" + }, + "migration-summary": { + "close": "Close", + "migration-successful": "Migration Successful!", + "title-migration-summary": "Migration summary" } }, "app-chrome": { @@ -893,6 +923,37 @@ "trace-id": "Trace ID: {{traceId}}" } }, + "auth-config": { + "auth-config-page-unconnected": { + "auth-settings": "Auth settings" + }, + "auth-drawer-unconnected": { + "advanced-auth": "Advanced Auth", + "enable-insecure-email-lookup": "Enable insecure email lookup", + "reset": "Reset", + "title-auth-settings": "Auth settings" + }, + "configure-auth-cta": { + "authentication-configuration-created-moment": "You have no authentication configuration created at the moment.", + "configuration-required": "Configuration required", + "refer-documentation-configure-authentication": "Refer to the documentation on how to configure authentication" + }, + "provider-config-form": { + "additional-actions-menu": { + "label-reset-to-default-values": "Reset to default values" + }, + "discard": "Discard", + "label-enabled": "Enabled", + "reset-configuration": "Are you sure you want to reset this configuration?", + "title-more-actions": "More actions", + "title-reset": "Reset", + "tooltip-more-actions": "More actions" + }, + "server-discovery-modal": { + "label-the-wellknownopenidconfiguration-endpoint-for-your-id-p": "The .well-known/openid-configuration endpoint for your IdP", + "title-open-id-connect-discovery-url": "OpenID Connect Discovery URL" + } + }, "bookmarks-page": { "empty": { "message": "It looks like you haven’t created any bookmarks yet", @@ -1069,7 +1130,16 @@ "request-data-source": "Request a new data source", "roadmap": "View roadmap" }, + "connections-redirect-notice": { + "aria-label-link-to-connections": "Link to Connections", + "go-to-connections": "Go to connections" + }, + "no-access-modal": { + "connection-contact-grafana-admin": "To add a new connection, contact your Grafana admin.", + "okay": "Okay" + }, "search": { + "aria-label-search-all": "Search all", "placeholder": "Search all" } }, @@ -1880,12 +1950,97 @@ } }, "explore": { + "accordian-references": { + "references": "References" + }, "add-to-dashboard": "Add to dashboard", + "basic-extensions": { + "aria-label-add": "Add" + }, + "confirm-navigation-modal": { + "cancel": "Cancel", + "open": "Open", + "open-in-new-tab": "Open in new tab" + }, + "content-outline-item-button": { + "body": { + "aria-label-content-outline-item-collapse-button": "Content outline item collapse button" + } + }, + "correlation-editor-mode-bar": { + "exit-correlation-editor": "Exit correlation editor", + "save": "Save" + }, + "correlation-helper": { + "add-transformation": "Add transformation", + "aria-label-delete-transformation": "Delete transformation", + "aria-label-edit-transformation": "Edit transformation", + "label-description": "Description", + "label-label": "Label", + "title-correlation-details": "Correlation details" + }, + "correlation-transformation-add-modal": { + "aria-label-field": "Field", + "aria-label-type": "Type", + "cancel": "Cancel", + "label-expression": "Expression", + "label-field": "Field", + "label-type": "Type", + "label-variable-name": "Variable name" + }, + "correlation-unsaved-changes-modal": { + "cancel": "Cancel", + "continue-without-saving": "Continue without saving", + "save-correlation": "Save correlation" + }, + "draggable-manager-demo": { + "click-horizontally-somewhere-colored-below": "Click and drag horizontally somewhere in the colored area, below.", + "draggable-manager-demo": "DraggableManager demo", + "dragging-a-divider": "Dragging a divider", + "dragging-a-sub-region": "Dragging a sub-region" + }, "drilldownInfo": { "action": "Go to Grafana Drilldown", "description": "Looking for the Grafana Explore apps? They are now called the Grafana Drilldown apps and can be found under <1>Menu > Drilldown", "title": "Explore Metrics, Logs, Traces and Profiles have moved!" }, + "explore": { + "title-flame-graph": "Flame graph", + "title-graph": "Graph", + "title-logs": "Logs", + "title-logs-sample": "Logs sample", + "title-node-graph": "Node graph", + "title-queries": "Queries", + "title-raw-prometheus": "Raw Prometheus", + "title-table": "Table", + "title-traces": "Traces" + }, + "explore-run-query-button": { + "run-button": { + "aria-label-run-query-options": "Run query options" + } + }, + "explore-toolbar": { + "outline": "Outline", + "tooltip-content-outline": "Content outline" + }, + "get-matches-metadata": { + "matches": "0 matches" + }, + "legacy-create-span-link-factory": { + "title-explore-metrics-for-this-span": "Explore metrics for this span", + "title-explore-split": "Explore the logs for this in split view", + "title-session-for-this-span": "Session for this span" + }, + "live-logs": { + "clear-logs": "Clear logs", + "exit-live-mode": "Exit live mode" + }, + "live-tail-button": { + "pause-the-live-stream": "Pause the live stream", + "start-live-stream-your-logs": "Start live stream your logs", + "stop-and-exit-the-live-stream": "Stop and exit the live stream" + }, "logs": { "logs-volume": { "add-filters": "Add more labels to your query to narrow down your search.", @@ -1897,6 +2052,63 @@ "scan-for-older-logs": "Scan for older logs", "stop-scan": "Stop scan" }, + "logs-container": { + "label-logs": "Logs" + }, + "logs-meta-row": { + "download": "Download", + "show-original-line": "Show original line" + }, + "logs-sample-panel": { + "logs-sample-is-loading": "Logs sample is loading...", + "no-logs-sample-data": "No logs sample data.", + "open-in-split-view-button": { + "open-logs-in-split-view": "Open logs in split view" + }, + "title-failed-sample-query": "Failed to load logs sample for this query" + }, + "logs-table-empty-fields": { + "no-fields": "No fields" + }, + "logs-table-multi-select": { + "fields": "Fields" + }, + "logs-table-nav-field": { + "aria-label-drag-and-drop-icon": "Drag and drop icon", + "title-drag-and-drop-to-reorder": "Drag and drop to reorder" + }, + "logs-table-wrap": { + "label-select-query": "Select query", + "tooltip-select-query-visualize-table": "Select a query to visualize in the table" + }, + "logs-volume-panel-list": { + "label-reload-log-volume": "Reload log volume", + "loading": "Loading...", + "title-failed-volume-query": "Failed to load log volume for this query", + "title-no-logs-volume-available": "No logs volume available", + "title-showing-partial-data": "Showing partial data", + "title-unable-to-show-log-volume": "Unable to show log volume" + }, + "make-datasource-setup": { + "aria-label-query": "query" + }, + "next": "Next", + "no-data-source-call-to-action": { + "cta-element": { + "add-data-source": "Add data source" + }, + "footer": { + "learn-more": "Learn more", + "pro-tip-define-sources-through-configuration-files": " ProTip: You can also define data sources through configuration files. " + } + }, + "prev": "Prev", + "queryless-apps-extensions": { + "aria-label-go-queryless": "Go queryless" + }, + "raw-list-item": { + "tooltip-copy-to-clipboard": "Copy to clipboard" + }, "rich-history": { "close-tooltip": "Close query history", "datasource-a-z": "Data source A-Z", @@ -2003,6 +2215,12 @@ "run-query-button": "Run query", "switch-datasource-button": "Switch data source and run query" }, + "search-bar-input": { + "placeholder-find": "Find...", + "suffix": { + "tooltip-clear-input": "Clear input" + } + }, "secondary-actions": { "add-from-query-library": "Add query from library", "query-add-button": "Add query", @@ -2012,11 +2230,66 @@ "query-inspector-button": "Query inspector", "query-inspector-button-aria-label": "Query inspector" }, + "span-bar-section": { + "title-span-bar": "Span bar" + }, + "span-bar-settings": { + "label-label": "Label", + "label-tag-key": "Tag key", + "placeholder-duration": "Duration", + "placeholder-enter-tag-key": "Enter tag key", + "tooltip-default-duration": "Default: duration" + }, + "span-detail": { + "label-resource-attributes": "Resource attributes", + "label-span-attributes": "Span attributes", + "label-stack-trace": "Stack trace", + "warnings": "Warnings" + }, + "span-filters": { + "aria-label-select-max-span-operator": "Select max span operator", + "aria-label-select-min-span-operator": "Select min span operator", + "aria-label-select-service-name": "Select service name", + "aria-label-select-service-name-operator": "Select service name operator", + "aria-label-select-span-name": "Select span name", + "aria-label-select-span-name-operator": "Select span name operator", + "label-duration": "Duration", + "label-service-name": "Service name", + "label-span-name": "Span name", + "label-tags": "Tags", + "placeholder-all-service-names": "All service names", + "placeholder-all-span-names": "All span names" + }, + "span-filters-tags": { + "aria-label-add-tag": "Add tag", + "aria-label-input-tag-value": "Input tag value", + "aria-label-remove-tag": "Remove tag", + "aria-label-select-tag-key": "Select tag key", + "aria-label-select-tag-operator": "Select tag operator", + "aria-label-select-tag-value": "Select tag value", + "placeholder-select-tag": "Select tag", + "placeholder-select-value": "Select value", + "placeholder-tag-value": "Tag value", + "tooltip-add-tag": "Add tag", + "tooltip-remove-tag": "Remove tag" + }, + "span-flame-graph": { + "flame-graph": "Flame graph" + }, + "supplementary-result-error": { + "show-details": "Show details" + }, "table": { "no-data": "0 series returned", "title": "Table", "title-with-name": "Table - {{name}}" }, + "timeline-collapser": { + "tooltip-collapse": "Collapse +1", + "tooltip-collapse-all": "Collapse all", + "tooltip-expand": "Expand +1", + "tooltip-expand-all": "Expand all" + }, "toolbar": { "add-to-extensions": "Add", "add-to-queryless-extensions": "Go queryless", @@ -2037,6 +2310,32 @@ "split-title": "Split", "split-tooltip": "Split the pane", "split-widen": "Widen pane" + }, + "trace-page-actions": { + "give-feedback": "Give feedback", + "title-share-thoughts-about-tracing-grafana": "Share your thoughts about tracing in Grafana." + }, + "trace-view": { + "no-data": "No data" + }, + "trace-view-container": { + "title-trace": "Trace" + }, + "unthemed-logs": { + "label-deduplication": "Deduplication", + "label-display-results": "Display results", + "label-prettify-json": "Prettify JSON", + "label-time": "Time", + "label-unique-labels": "Unique labels", + "label-wrap-lines": "Wrap lines", + "title-feedback": "Feedback", + "title-logs-volume": "Logs volume" + }, + "unthemed-viewing-layer": { + "reset-selection": "Reset selection" + }, + "unthemed-virtualized-trace-view": { + "title-scroll-to-top": "Scroll to top" } }, "explore-metrics": { From 9e9eb7a4f8ef2d7df51734b401956b33d768f198 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 24 Mar 2025 11:57:37 +0000 Subject: [PATCH 26/86] Chore: add new theme event, modify preference type in drawer (#102523) * add new theme event, modify preference type in drawer * update to theme_drawer --- .../core/components/SharedPreferences/SharedPreferences.tsx | 5 +++++ .../core/components/ThemeSelector/ThemeSelectorDrawer.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index a7dcaf1e293..d4e4d669841 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -120,6 +120,11 @@ export class SharedPreferences extends PureComponent { if (confirmationResult) { const { homeDashboardUID, theme, timezone, weekStart, language, queryHistory, navbar } = this.state; + reportInteraction('grafana_preferences_save_button_clicked', { + preferenceType: this.props.preferenceType, + theme, + language, + }); await this.service.update({ homeDashboardUID, theme, timezone, weekStart, language, queryHistory, navbar }); window.location.reload(); } diff --git a/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx b/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx index a633bdb3f53..32a2e869b90 100644 --- a/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx +++ b/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx @@ -22,7 +22,7 @@ export function ThemeSelectorDrawer({ onClose }: Props) { const onChange = (theme: ThemeRegistryItem) => { reportInteraction('grafana_preferences_theme_changed', { toTheme: theme.id, - preferenceType: 'user', + preferenceType: 'theme_drawer', }); changeTheme(theme.id, false); }; From 6f2a9abc0353829869ea9dcbb90a6576c8a41524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Mon, 24 Mar 2025 13:20:00 +0100 Subject: [PATCH 27/86] i18n: consolidate i18n types & runtime services (#102535) * i18n: consolidate i18n types & runtime services * Chore: updates after PR feedback * Chore: updates after feedback * Chore: updates after feedback * Chore: updates after PR feedback * Chore: fix i18n * Chore: updates after PR feedback --- .betterer.results | 9 +- packages/grafana-runtime/src/types/i18n.ts | 63 +++++++++++++ packages/grafana-runtime/src/unstable.ts | 3 +- packages/grafana-runtime/src/utils/i18n.ts | 21 ----- packages/grafana-runtime/src/utils/i18n.tsx | 57 ++++++++++++ public/app/app.ts | 4 +- .../core/internationalization/index.test.tsx | 92 ++++++++++++++++++- .../app/core/internationalization/index.tsx | 29 ++++-- public/locales/en-US/grafana.json | 3 + 9 files changed, 245 insertions(+), 36 deletions(-) create mode 100644 packages/grafana-runtime/src/types/i18n.ts delete mode 100644 packages/grafana-runtime/src/utils/i18n.ts create mode 100644 packages/grafana-runtime/src/utils/i18n.tsx diff --git a/.betterer.results b/.betterer.results index b93947f59dd..6c13f9b1ddb 100644 --- a/.betterer.results +++ b/.betterer.results @@ -800,8 +800,7 @@ exports[`better eslint`] = { "public/app/app.ts:5381": [ [0, 0, 0, "\'@grafana/runtime/src/components/PanelDataErrorView\' import is restricted from being used by a pattern. Import from the public export instead.", "0"], [0, 0, 0, "\'@grafana/runtime/src/components/PanelRenderer\' import is restricted from being used by a pattern. Import from the public export instead.", "1"], - [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "2"], - [0, 0, 0, "\'@grafana/runtime/src/unstable\' import is restricted from being used by a pattern. Import from the public export instead.", "3"] + [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "2"] ], "public/app/core/TableModel.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -1016,6 +1015,12 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`profiler\`)", "6"], [0, 0, 0, "Do not re-export imported variable (\`updateLegendValues\`)", "7"] ], + "public/app/core/internationalization/index.test.tsx:5381": [ + [0, 0, 0, "\'@grafana/runtime/src/unstable\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + ], + "public/app/core/internationalization/index.tsx:5381": [ + [0, 0, 0, "\'@grafana/runtime/src/unstable\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] + ], "public/app/core/navigation/GrafanaRouteError.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"] diff --git a/packages/grafana-runtime/src/types/i18n.ts b/packages/grafana-runtime/src/types/i18n.ts new file mode 100644 index 00000000000..31879879a5f --- /dev/null +++ b/packages/grafana-runtime/src/types/i18n.ts @@ -0,0 +1,63 @@ +/** + * Hook type for translation function that takes an ID, default message, and optional values + * @returns A function that returns the translated string + */ +type UseTranslateHook = () => (id: string, defaultMessage: string, values?: Record) => string; + +/** + * Type for children elements in Trans component + * Can be either React nodes or an object of values + */ +type TransChild = React.ReactNode | Record; + +/** + * Props interface for the Trans component used for internationalization + */ +interface TransProps { + /** + * The translation key to look up + */ + i18nKey: string; + /** + * Child elements or values to interpolate + */ + children?: TransChild | readonly TransChild[]; + /** + * React elements to use for interpolation + */ + components?: readonly React.ReactElement[] | { readonly [tagName: string]: React.ReactElement }; + /** + * Count value for pluralization + */ + count?: number; + /** + * Default text if translation is not found + */ + defaults?: string; + /** + * Namespace for the translation key + */ + ns?: string; + /** + * Whether to unescape HTML entities + */ + shouldUnescape?: boolean; + /** + * Values to interpolate into the translation + */ + values?: Record; +} + +/** + * Function declaration for the Trans component + * @param props - The TransProps object containing translation configuration + * @returns A React element with translated content + */ +declare function Trans(props: TransProps): React.ReactElement; + +/** + * Type alias for the Trans component + */ +type TransType = typeof Trans; + +export type { UseTranslateHook, TransProps, TransType }; diff --git a/packages/grafana-runtime/src/unstable.ts b/packages/grafana-runtime/src/unstable.ts index 84075dbb08a..07bb817ac5b 100644 --- a/packages/grafana-runtime/src/unstable.ts +++ b/packages/grafana-runtime/src/unstable.ts @@ -9,4 +9,5 @@ * and be subject to the standard policies */ -export { useTranslate, setUseTranslateHook } from './utils/i18n'; +export { useTranslate, setUseTranslateHook, setTransComponent, Trans } from './utils/i18n'; +export type { TransProps } from './types/i18n'; diff --git a/packages/grafana-runtime/src/utils/i18n.ts b/packages/grafana-runtime/src/utils/i18n.ts deleted file mode 100644 index f67f63316e5..00000000000 --- a/packages/grafana-runtime/src/utils/i18n.ts +++ /dev/null @@ -1,21 +0,0 @@ -type UseTranslateHook = () => (id: string, defaultMessage: string, values?: Record) => string; - -/** - * Provides a i18next-compatible translation function. - */ -export let useTranslate: UseTranslateHook = () => { - // Fallback implementation that should be overridden by setUseT - const errorMessage = 'useTranslate is not set. useTranslate must not be called before Grafana is initialized.'; - if (process.env.NODE_ENV === 'development') { - throw new Error(errorMessage); - } - - console.error(errorMessage); - return (id: string, defaultMessage: string) => { - return defaultMessage; - }; -}; - -export function setUseTranslateHook(hook: UseTranslateHook) { - useTranslate = hook; -} diff --git a/packages/grafana-runtime/src/utils/i18n.tsx b/packages/grafana-runtime/src/utils/i18n.tsx new file mode 100644 index 00000000000..30c06263dfc --- /dev/null +++ b/packages/grafana-runtime/src/utils/i18n.tsx @@ -0,0 +1,57 @@ +import { type TransProps, type TransType, type UseTranslateHook } from '../types/i18n'; + +/** + * Provides a i18next-compatible translation function. + */ +export let useTranslate: UseTranslateHook = useTranslateDefault; + +function useTranslateDefault() { + // Fallback implementation that should be overridden by setUseT + const errorMessage = 'useTranslate is not set. useTranslate must not be called before Grafana is initialized.'; + if (process.env.NODE_ENV === 'development') { + throw new Error(errorMessage); + } + + console.error(errorMessage); + return (id: string, defaultMessage: string) => { + return defaultMessage; + }; +} + +export function setUseTranslateHook(hook: UseTranslateHook) { + useTranslate = hook; +} + +let TransComponent: TransType | undefined; + +/** + * Sets the Trans component that will be used for translations throughout the application. + * This function should only be called once during application initialization. + * + * @param transComponent - The Trans component function to use for translations + * @throws {Error} If called multiple times outside of test environment + */ +export function setTransComponent(transComponent: TransType) { + // We allow overriding the trans component in tests + if (TransComponent && process.env.NODE_ENV !== 'test') { + throw new Error('setTransComponent() function should only be called once, when Grafana is starting.'); + } + + TransComponent = transComponent; +} + +/** + * A React component for handling translations with support for interpolation and pluralization. + * This component must be initialized using setTransComponent before use. + * + * @param props - The translation props including the i18nKey and any interpolation values + * @returns A React element containing the translated content + * @throws {Error} If the Trans component hasn't been initialized + */ +export function Trans(props: TransProps): React.ReactElement { + if (!TransComponent) { + throw new Error('Trans component not set. Use setTransComponent to set the Trans component.'); + } + + return ; +} diff --git a/public/app/app.ts b/public/app/app.ts index f9e65818a3d..f576ba415a0 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -45,7 +45,6 @@ import { import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView'; import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer'; import { setPluginPage } from '@grafana/runtime/src/components/PluginPage'; -import { setUseTranslateHook } from '@grafana/runtime/src/unstable'; import config, { updateConfig } from 'app/core/config'; import { getStandardTransformers } from 'app/features/transformers/standardTransformers'; @@ -59,7 +58,7 @@ import { getAllOptionEditors, getAllStandardFieldConfigs } from './core/componen import { PluginPage } from './core/components/Page/PluginPage'; import { GrafanaContextType, useChromeHeaderHeight, useReturnToPreviousInternal } from './core/context/GrafanaContext'; import { initializeCrashDetection } from './core/crash'; -import { initializeI18n, useTranslateInternal } from './core/internationalization'; +import { initializeI18n } from './core/internationalization'; import { setMonacoEnv } from './core/monacoEnv'; import { interceptLinkClicks } from './core/navigation/patch/interceptLinkClicks'; import { CorrelationsService } from './core/services/CorrelationsService'; @@ -254,7 +253,6 @@ export class GrafanaApp { setReturnToPreviousHook(useReturnToPreviousInternal); setChromeHeaderHeightHook(useChromeHeaderHeight); - setUseTranslateHook(useTranslateInternal); if (config.featureToggles.crashDetection) { initializeCrashDetection(); diff --git a/public/app/core/internationalization/index.test.tsx b/public/app/core/internationalization/index.test.tsx index 8eabddb6f15..8dab4d6e250 100644 --- a/public/app/core/internationalization/index.test.tsx +++ b/public/app/core/internationalization/index.test.tsx @@ -1,6 +1,41 @@ import { render } from '@testing-library/react'; +import { I18nextProvider } from 'react-i18next'; -import { Trans } from './index'; +import { PluginContextProvider, PluginMeta, PluginType } from '@grafana/data'; +import { + Trans as PluginTrans, + setTransComponent, + setUseTranslateHook, + useTranslate, +} from '@grafana/runtime/src/unstable'; + +import { getI18next, Trans, useTranslateInternal } from './index'; + +const id = 'frontend-test-locales-plugin'; +const mockedMeta: PluginMeta = { + id, + name: 'Frontend Test Locales Plugin', + type: PluginType.panel, + info: { + author: { name: 'Test Author' }, + description: 'Test Description', + links: [], + logos: { + large: 'test-plugin-large-logo', + small: 'test-plugin-small-logo', + }, + screenshots: [], + version: '1.0.0', + updated: '2021-01-01', + }, + module: 'test-plugin', + baseUrl: 'test-plugin', +}; + +const DummyUseTranslateComponent = () => { + const t = useTranslate(); + return
{t('frontendtests.test-key', 'test-key not found')}
; +}; describe('internationalization', () => { describe('Trans component', () => { @@ -22,4 +57,59 @@ describe('internationalization', () => { expect(getByText('Table - <script></script>')).toBeInTheDocument(); }); }); + describe('for plugins', () => { + beforeEach(() => { + getI18next().addResourceBundle('en', id, { 'frontendtests.test-key': 'test-value' }, undefined, true); + setTransComponent(Trans); + setUseTranslateHook(useTranslateInternal); + }); + + it('should return the correct value when using Trans component within a plugin context', async () => { + const { getByText, queryByText } = render( + + + + + + ); + + expect(getByText('test-value')).toBeInTheDocument(); + expect(queryByText('test-key not found')).not.toBeInTheDocument(); + }); + + it('should return the correct value when using Trans component without a plugin context', async () => { + const { getByText, queryByText } = render( + + + + ); + + expect(getByText('test-key not found')).toBeInTheDocument(); + expect(queryByText('test-value')).not.toBeInTheDocument(); + }); + + it('should return the correct value when using useTranslate hook within a plugin context', async () => { + const { getByText, queryByText } = render( + + + + + + ); + + expect(getByText('test-value')).toBeInTheDocument(); + expect(queryByText('test-key not found')).not.toBeInTheDocument(); + }); + + it('should return the correct value when using useTranslate hook without a plugin context', async () => { + const { getByText, queryByText } = render( + + + + ); + + expect(getByText('test-key not found')).toBeInTheDocument(); + expect(queryByText('test-value')).not.toBeInTheDocument(); + }); + }); }); diff --git a/public/app/core/internationalization/index.tsx b/public/app/core/internationalization/index.tsx index 821887eb53a..73242d04728 100644 --- a/public/app/core/internationalization/index.tsx +++ b/public/app/core/internationalization/index.tsx @@ -1,8 +1,11 @@ import i18n, { InitOptions, TFunction } from 'i18next'; import LanguageDetector, { DetectorOptions } from 'i18next-browser-languagedetector'; -import { ReactElement } from 'react'; +import { ReactElement, useMemo } from 'react'; import { Trans as I18NextTrans, initReactI18next } from 'react-i18next'; // eslint-disable-line no-restricted-imports +import { usePluginContext } from '@grafana/data'; +import { setTransComponent, setUseTranslateHook, TransProps } from '@grafana/runtime/src/unstable'; + import { DEFAULT_LANGUAGE, NAMESPACES, VALID_LANGUAGES } from './constants'; import { loadTranslations } from './loadTranslations'; @@ -59,6 +62,9 @@ export async function initializeI18n(language: string): Promise<{ language: stri tFunc = i18n.getFixedT(null, NAMESPACES); + setUseTranslateHook(useTranslateInternal); + setTransComponent(Trans); + return { language: i18nInstance.resolvedLanguage, }; @@ -69,14 +75,14 @@ export function changeLanguage(locale: string) { return i18n.changeLanguage(validLocale); } -type I18NextTransType = typeof I18NextTrans; -type I18NextTransProps = Parameters[0]; +export const Trans = (props: TransProps): ReactElement => { + const context = usePluginContext(); -interface TransProps extends I18NextTransProps { - i18nKey: string; -} + // If we are in a plugin context, use the plugin's id as the namespace + if (context?.meta?.id) { + return ; + } -export const Trans = (props: TransProps): ReactElement => { return ; }; @@ -131,5 +137,12 @@ export function getI18next() { // Perhaps in the future this will use useTranslation from react-i18next or something else // from context export function useTranslateInternal() { - return t; + const context = usePluginContext(); + if (!context) { + return t; + } + + const { meta } = context; + const pluginT = useMemo(() => getI18next().getFixedT(null, meta.id), [meta.id]); + return pluginT; } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 774dba60d03..301e0a5ddc2 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2392,6 +2392,9 @@ "description": "Changes that you made may not be saved.", "discard-button": "Discard unsaved changes" }, + "frontendtests": { + "test-key": "test-key not found" + }, "gen-ai": { "apply-suggestion": "Apply", "incomplete-request-error": "Sorry, I was unable to complete your request. Please try again.", From c20de2b7532390a58b6c999c77238dcb79b1b104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 24 Mar 2025 13:50:37 +0100 Subject: [PATCH 28/86] Tabs: Support for nested url sync (#102670) --- .../scene/layout-rows/RowItem.tsx | 5 +++ .../scene/layout-tabs/TabItem.tsx | 5 +++ .../scene/layout-tabs/TabItemRenderer.tsx | 4 +- .../layout-tabs/TabsLayoutManager.test.tsx | 45 +++++++++++++++++++ .../scene/layout-tabs/TabsLayoutManager.tsx | 43 ++++++++++++++---- 5 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.test.tsx diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx index 8d3b8f91a75..2136356bad7 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx @@ -1,5 +1,6 @@ import { sceneGraph, SceneObject, SceneObjectBase, SceneObjectState, VariableDependencyConfig } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; +import kbn from 'app/core/utils/kbn'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { ConditionalRendering } from '../../conditional-rendering/ConditionalRendering'; @@ -72,6 +73,10 @@ export class RowItem return this.state.layout; } + public getSlug(): string { + return kbn.slugifyForUrl(sceneGraph.interpolate(this, this.state.title ?? 'Row')); + } + public switchLayout(layout: DashboardLayoutManager) { this.setState({ layout: this._layoutRestorer.getLayout(layout, this.state.layout) }); } diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItem.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItem.tsx index df09763b00b..6bef7c0bf8d 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItem.tsx @@ -1,5 +1,6 @@ import { SceneObjectState, SceneObjectBase, sceneGraph, VariableDependencyConfig, SceneObject } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; +import kbn from 'app/core/utils/kbn'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { getDefaultVizPanel } from '../../utils/utils'; @@ -53,6 +54,10 @@ export class TabItem return this.state.layout; } + public getSlug(): string { + return kbn.slugifyForUrl(sceneGraph.interpolate(this, this.state.title ?? 'Tab')); + } + public switchLayout(layout: DashboardLayoutManager) { this.setState({ layout: this._layoutRestorer.getLayout(layout, this.state.layout) }); } diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx index e3b7d66afdd..f0b5c1080ed 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx @@ -13,10 +13,12 @@ export function TabItemRenderer({ model }: SceneComponentProps) { const { tabs, currentTabIndex } = parentLayout.useState(); const titleInterpolated = sceneGraph.interpolate(model, title, undefined, 'text'); const { isSelected, onSelect, isSelectable } = useElementSelection(key); + const mySlug = model.getSlug(); + const urlKey = parentLayout.getUrlKey(); const myIndex = tabs.findIndex((tab) => tab === model); const isActive = myIndex === currentTabIndex; const location = useLocation(); - const href = textUtil.sanitize(locationUtil.getUrlForPartial(location, { tab: myIndex })); + const href = textUtil.sanitize(locationUtil.getUrlForPartial(location, { [urlKey]: mySlug })); return ( { + describe('url sync', () => { + it('when on top level', () => { + const tabsLayoutManager = new TabsLayoutManager({ + tabs: [new TabItem({ title: 'Performance' })], + }); + + const urlState = tabsLayoutManager.getUrlState(); + expect(urlState).toEqual({ dtab: 'performance' }); + }); + + it('when nested under row and parent tab', () => { + const innerMostTabs = new TabsLayoutManager({ + tabs: [new TabItem({ title: 'Performance' })], + }); + + new RowsLayoutManager({ + rows: [ + new RowItem({ + title: 'Overview', + layout: new TabsLayoutManager({ + tabs: [ + new TabItem({ + title: 'Frontend', + layout: innerMostTabs, + }), + ], + }), + }), + ], + }); + + const urlState = innerMostTabs.getUrlState(); + expect(urlState).toEqual({ + ['overview-frontend-dtab']: 'performance', + }); + }); + }); +}); diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx index 71af05bfce7..f4369290323 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx @@ -12,6 +12,7 @@ import { ObjectRemovedFromCanvasEvent, ObjectsReorderedOnCanvasEvent, } from '../../edit-pane/shared'; +import { RowItem } from '../layout-rows/RowItem'; import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -44,7 +45,7 @@ export class TabsLayoutManager extends SceneObjectBase i public readonly descriptor = TabsLayoutManager.descriptor; - protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: ['tab'] }); + protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: () => [this.getUrlKey()] }); public constructor(state: Partial) { super({ @@ -65,19 +66,23 @@ export class TabsLayoutManager extends SceneObjectBase i } public getUrlState() { - return { tab: this.state.currentTabIndex.toString() }; + const key = this.getUrlKey(); + return { [key]: this.getCurrentTab().getSlug() }; } public updateFromUrl(values: SceneObjectUrlValues) { - if (!values.tab) { + const key = this.getUrlKey(); + const urlValue = values[key]; + + if (!urlValue) { return; } - if (typeof values.tab === 'string') { - const tabIndex = parseInt(values.tab, 10); - if (this.state.tabs[tabIndex]) { - this.setState({ currentTabIndex: tabIndex }); - } else { - this.setState({ currentTabIndex: 0 }); + + if (typeof values[key] === 'string') { + // find tab with matching slug + const matchIndex = this.state.tabs.findIndex((tab) => tab.getSlug() === urlValue); + if (matchIndex !== -1) { + this.setState({ currentTabIndex: matchIndex }); } } } @@ -212,4 +217,24 @@ export class TabsLayoutManager extends SceneObjectBase i return new TabsLayoutManager({ tabs }); } + + getUrlKey(): string { + let parent = this.parent; + // Panel edit uses tab key already so we are using dtab here to not conflict + let key = 'dtab'; + + while (parent) { + if (parent instanceof TabItem) { + key = `${parent.getSlug()}-${key}`; + } + + if (parent instanceof RowItem) { + key = `${parent.getSlug()}-${key}`; + } + + parent = parent.parent; + } + + return key; + } } From f0db0c4f0d1e2e80d5e1066fb35d065dcbdb6cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 24 Mar 2025 13:51:21 +0100 Subject: [PATCH 29/86] Dashboards: Rows layout polish and fixes (#102666) --- .../src/themes/GlobalStyles/dashboardGrid.ts | 8 +++ .../dashboard-scene/scene/DashboardScene.tsx | 10 ++-- .../ResponsiveGridItemRenderer.tsx | 9 ++-- .../scene/layout-rows/RowItem.tsx | 6 +-- .../scene/layout-rows/RowItemEditor.tsx | 18 +++---- .../scene/layout-rows/RowItemMenu.tsx | 4 +- .../scene/layout-rows/RowItemRenderer.tsx | 49 +++++++++++++------ .../features/dashboard-scene/utils/utils.ts | 12 +---- public/locales/en-US/grafana.json | 7 +-- 9 files changed, 63 insertions(+), 60 deletions(-) diff --git a/packages/grafana-ui/src/themes/GlobalStyles/dashboardGrid.ts b/packages/grafana-ui/src/themes/GlobalStyles/dashboardGrid.ts index c351009525f..aa243c17c68 100644 --- a/packages/grafana-ui/src/themes/GlobalStyles/dashboardGrid.ts +++ b/packages/grafana-ui/src/themes/GlobalStyles/dashboardGrid.ts @@ -90,6 +90,14 @@ export function getDashboardGridStyles(theme: GrafanaTheme2) { }, }, + '.dashboard-canvas-add-button': { + opacity: 0, + + '&:hover': { + opacity: 1, + }, + }, + '.dashboard-visible-hidden-element': { opacity: 0.6, diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index b2de2c14ca0..d879f1d75c8 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -108,8 +108,6 @@ export interface DashboardSceneState extends SceneObjectState { controls?: DashboardControls; /** True when editing */ isEditing?: boolean; - /** Controls the visibility of hidden elements like row headers */ - showHiddenElements?: boolean; /** True when user made a change */ isDirty?: boolean; /** meta flags */ @@ -270,7 +268,7 @@ export class DashboardScene extends SceneObjectBase impleme this._initialUrlState = locationService.getLocation(); // Switch to edit mode - this.setState({ isEditing: true, showHiddenElements: true }); + this.setState({ isEditing: true }); // Propagate change edit mode change to children this.state.body.editModeChanged?.(true); @@ -355,10 +353,10 @@ export class DashboardScene extends SceneObjectBase impleme if (restoreInitialState) { // Restore initial state and disable editing - this.setState({ ...this._initialState, isEditing: false, showHiddenElements: false }); + this.setState({ ...this._initialState, isEditing: false }); } else { // Do not restore - this.setState({ isEditing: false, showHiddenElements: false }); + this.setState({ isEditing: false }); } // if we are in edit panel, we need to onDiscard() @@ -376,8 +374,6 @@ export class DashboardScene extends SceneObjectBase impleme return this._initialState !== undefined; } - public onToggleHiddenElements = () => this.setState({ showHiddenElements: !this.state.showHiddenElements }); - public pauseTrackingChanges() { this._changeTracker.stopTrackingChanges(); } diff --git a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridItemRenderer.tsx index 2e36f85e108..f2b8a6bcadc 100644 --- a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridItemRenderer.tsx @@ -10,24 +10,23 @@ export interface ResponsiveGridItemProps extends SceneComponentProps {model.state.repeatedPanels.map((item) => ( -
+
))} ) : ( -
+
); diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx index 2136356bad7..8bdc2ad2163 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx @@ -23,7 +23,7 @@ export interface RowItemState extends SceneObjectState { title?: string; isCollapsed?: boolean; isHeaderHidden?: boolean; - height?: 'expand' | 'min'; + fillScreen?: boolean; conditionalRendering?: ConditionalRendering; } @@ -141,8 +141,8 @@ export class RowItem this.setState({ isHeaderHidden }); } - public onChangeHeight(height: 'expand' | 'min') { - this.setState({ height }); + public onChangeFillScreen(fillScreen: boolean) { + this.setState({ fillScreen }); } public onChangeRepeat(repeat: string | undefined) { diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx index 4bfc3b77544..1abade8a396 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx @@ -1,8 +1,7 @@ import { useMemo } from 'react'; -import { SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { Alert, Input, RadioButtonGroup, Switch, TextLink } from '@grafana/ui'; +import { Alert, Input, Switch, TextLink } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; @@ -31,8 +30,8 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] ) .addItem( new OptionsPaneItemDescriptor({ - title: t('dashboard.rows-layout.row-options.row.height', 'Height'), - render: () => , + title: t('dashboard.rows-layout.row-options.row.fill-screen', 'Fill screen'), + render: () => , }) ) .addItem( @@ -99,15 +98,10 @@ function RowHeaderSwitch({ row }: { row: RowItem }) { return row.onHeaderHiddenToggle()} />; } -function RowHeightSelect({ row }: { row: RowItem }) { - const { height = 'min' } = row.useState(); +function FillScreenSwitch({ row }: { row: RowItem }) { + const { fillScreen } = row.useState(); - const options: Array> = [ - { label: t('dashboard.rows-layout.options.height-expand', 'Expand'), value: 'expand' }, - { label: t('dashboard.rows-layout.options.height-min', 'Min'), value: 'min' }, - ]; - - return row.onChangeHeight(option)} />; + return row.onChangeFillScreen(!fillScreen)} />; } function RowRepeatSelect({ row }: { row: RowItem }) { diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemMenu.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemMenu.tsx index 1243c07d731..0782595ba45 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemMenu.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemMenu.tsx @@ -1,4 +1,4 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Dropdown, Menu, ToolbarButtonRow, useStyles2 } from '@grafana/ui'; @@ -14,7 +14,7 @@ export function RowItemMenu({ model }: RowItemMenuProps) { const styles = useStyles2(getStyles); return ( - + ( diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx index e69e6d8d407..4e6eb2df022 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx @@ -4,7 +4,7 @@ import { useCallback, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { SceneComponentProps } from '@grafana/scenes'; -import { clearButtonStyles, Icon, useStyles2 } from '@grafana/ui'; +import { clearButtonStyles, Icon, Tooltip, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { useIsClone } from '../../utils/clone'; @@ -19,25 +19,24 @@ import { RowItem } from './RowItem'; import { RowItemMenu } from './RowItemMenu'; export function RowItemRenderer({ model }: SceneComponentProps) { - const { layout, isCollapsed, height = 'min', isHeaderHidden } = model.useState(); + const { layout, isCollapsed, fillScreen, isHeaderHidden } = model.useState(); const isClone = useIsClone(model); - const { isEditing, showHiddenElements } = useDashboardState(model); + const { isEditing } = useDashboardState(model); const isConditionallyHidden = useIsConditionallyHidden(model); const { isSelected, onSelect, isSelectable } = useElementSelectionScene(model); const title = useInterpolatedTitle(model); const styles = useStyles2(getStyles); const clearStyles = useStyles2(clearButtonStyles); - const shouldGrow = !isCollapsed && height === 'expand'; - const isHiddenButVisibleElement = showHiddenElements && isConditionallyHidden; - const isHiddenButVisibleHeader = showHiddenElements && isHeaderHidden; + const shouldGrow = !isCollapsed && fillScreen; + const isHidden = isConditionallyHidden && !isEditing; // Highlight the full row when hovering over header const [selectableHighlight, setSelectableHighlight] = useState(false); const onHeaderEnter = useCallback(() => setSelectableHighlight(true), []); const onHeaderLeave = useCallback(() => setSelectableHighlight(false), []); - if (isConditionallyHidden && !showHiddenElements) { + if (isHidden) { return null; } @@ -49,19 +48,24 @@ export function RowItemRenderer({ model }: SceneComponentProps) { isEditing && isCollapsed && styles.wrapperEditingCollapsed, isCollapsed && styles.wrapperCollapsed, shouldGrow && styles.wrapperGrow, - isHiddenButVisibleElement && 'dashboard-visible-hidden-element', + isConditionallyHidden && 'dashboard-visible-hidden-element', !isClone && isSelected && 'dashboard-selected-element', !isClone && !isSelected && selectableHighlight && 'dashboard-selectable-element' )} - onPointerDown={onSelect} + onPointerDown={(e) => { + // If we selected and are clicking a button inside row header then don't de-select row + if (isSelected && e.target instanceof Element && e.target.closest('button')) { + // Stop propagation otherwise dashboaed level onPointerDown will de-select row + e.stopPropagation(); + return; + } + + onSelect?.(e); + }} > - {(!isHeaderHidden || (isEditing && showHiddenElements)) && ( + {(!isHeaderHidden || isEditing) && (
@@ -76,8 +80,15 @@ export function RowItemRenderer({ model }: SceneComponentProps) { data-testid={selectors.components.DashboardRow.title(title!)} > - + {title} + {isHeaderHidden && ( + + + + )} {!isClone && isEditing && } @@ -108,6 +119,9 @@ function getStyles(theme: GrafanaTheme2) { gap: theme.spacing(1), }), rowTitle: css({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(2), fontSize: theme.typography.h5.fontSize, fontWeight: theme.typography.fontWeightMedium, whiteSpace: 'nowrap', @@ -117,6 +131,9 @@ function getStyles(theme: GrafanaTheme2) { flexGrow: 1, minWidth: 0, }), + rowTitleHidden: css({ + textDecoration: 'line-through', + }), wrapper: css({ display: 'flex', flexDirection: 'column', diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index 33321ca235b..c9b7e2b4a4e 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -446,17 +446,9 @@ export function useDashboard(scene: SceneObject): DashboardScene { return getDashboardSceneFor(scene); } -export function useDashboardState( - scene: SceneObject -): DashboardSceneState & { isEditing: boolean; showHiddenElements: boolean } { +export function useDashboardState(scene: SceneObject): DashboardSceneState { const dashboard = useDashboard(scene); - const state = dashboard.useState(); - - return { - ...state, - isEditing: !!state.isEditing, - showHiddenElements: !!(state.isEditing && state.showHiddenElements), - }; + return dashboard.useState(); } export function useIsConditionallyHidden(scene: RowItem | ResponsiveGridItem): boolean { diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 301e0a5ddc2..f0f84ff36a0 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1539,11 +1539,8 @@ }, "rows-layout": { "description": "Collapsable panel groups with headings", + "header-hidden-tooltip": "Row header only visible in edit mode", "name": "Rows", - "options": { - "height-expand": "Expand", - "height-min": "Min" - }, "row": { "collapse": "Collapse row", "expand": "Expand row", @@ -1571,7 +1568,7 @@ } }, "row": { - "height": "Height", + "fill-screen": "Fill screen", "hide-header": "Hide row header", "title": "Title" }, From 82ea562b7576b8a8df5cbee9c0ee72605f38270a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Jamr=C3=B3z?= Date: Mon, 24 Mar 2025 13:56:51 +0100 Subject: [PATCH 30/86] Tempo: Add support for ad-hoc filters (#102448) * Add support for ad-hoc filters * Handle enum tags when using ad hoc filters --- .../SearchTraceQLEditor/TraceQLSearch.tsx | 12 +- .../tempo/SearchTraceQLEditor/utils.test.ts | 29 --- .../tempo/SearchTraceQLEditor/utils.ts | 19 +- .../plugins/datasource/tempo/datasource.ts | 11 +- .../tempo/language_provider.test.ts | 233 ++++++++++++------ .../datasource/tempo/language_provider.ts | 46 +++- .../datasource/tempo/traceql/QueryEditor.tsx | 8 +- .../datasource/tempo/traceql/traceql.ts | 2 + 8 files changed, 224 insertions(+), 136 deletions(-) diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx index fee68d5c771..1391f620691 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx @@ -64,7 +64,9 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa const templateVariables = getTemplateSrv().getVariables(); useEffect(() => { - setTraceQlQuery(datasource.languageProvider.generateQueryFromFilters(interpolateFilters(query.filters || []))); + setTraceQlQuery( + datasource.languageProvider.generateQueryFromFilters({ traceqlFilters: interpolateFilters(query.filters || []) }) + ); }, [datasource.languageProvider, query, templateVariables]); const findFilter = useCallback((id: string) => query.filters?.find((f) => f.id === id), [query.filters]); @@ -123,7 +125,9 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa return traceQlQuery; } const filtersAfterRemoval = query.filters?.filter((f) => f.id !== filter.id) || []; - return datasource.languageProvider.generateQueryFromFilters(interpolateFilters(filtersAfterRemoval || [])); + return datasource.languageProvider.generateQueryFromFilters({ + traceqlFilters: interpolateFilters(filtersAfterRemoval || []), + }); }; return ( @@ -260,7 +264,9 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa }); onClearResults(); - const traceQlQuery = datasource.languageProvider.generateQueryFromFilters(query.filters || []); + const traceQlQuery = datasource.languageProvider.generateQueryFromFilters({ + traceqlFilters: query.filters || [], + }); onChange({ ...query, query: traceQlQuery, diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts index 57c2dadc2b1..df4974921aa 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts @@ -7,7 +7,6 @@ import { intrinsics } from '../traceql/traceql'; import { filterToQuerySection, - generateQueryFromAdHocFilters, getAllTags, getFilteredTags, getIntrinsicTags, @@ -22,34 +21,6 @@ const datasource: TempoDatasource = { } as unknown as TempoDatasource; const lp = new TempoLanguageProvider(datasource); -describe('generateQueryFromAdHocFilters generates the correct query for', () => { - it('an empty array', () => { - expect(generateQueryFromAdHocFilters([], lp)).toBe('{}'); - }); - - it('a filter with values', () => { - expect(generateQueryFromAdHocFilters([{ key: 'footag', operator: '=', value: 'foovalue' }], lp)).toBe( - '{footag="foovalue"}' - ); - }); - - it('two filters with values', () => { - expect( - generateQueryFromAdHocFilters( - [ - { key: 'footag', operator: '=', value: 'foovalue' }, - { key: 'bartag', operator: '=', value: '0' }, - ], - lp - ) - ).toBe('{footag="foovalue" && bartag=0}'); - }); - - it('a filter with intrinsic values', () => { - expect(generateQueryFromAdHocFilters([{ key: 'kind', operator: '=', value: 'server' }], lp)).toBe('{kind=server}'); - }); -}); - describe('gets correct tags', () => { const datasource: TempoDatasource = { search: { diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts index 5b4313ce6f2..1a588b4a717 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts @@ -1,6 +1,6 @@ import { startCase, uniq } from 'lodash'; -import { AdHocVariableFilter, ScopedVars, SelectableValue } from '@grafana/data'; +import { ScopedVars, SelectableValue } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { VariableFormatID } from '@grafana/schema'; @@ -81,23 +81,6 @@ export const filterToQuerySection = (f: TraceqlFilter, filters: TraceqlFilter[], return `${scopeHelper(f, lp)}${tagHelper(f, filters)}${f.operator}${valueHelper(f)}`; }; -export const generateQueryFromAdHocFilters = (filters: AdHocVariableFilter[], lp: TempoLanguageProvider) => { - return `{${filters - .filter((f) => f.key && f.operator && f.value) - .map((f) => `${f.key}${f.operator}${adHocValueHelper(f, lp)}`) - .join(' && ')}}`; -}; - -const adHocValueHelper = (f: AdHocVariableFilter, lp: TempoLanguageProvider) => { - if (lp.getIntrinsics().find((t) => t === f.key)) { - return f.value; - } - if (parseInt(f.value, 10).toString() === f.value) { - return f.value; - } - return `"${f.value}"`; -}; - export const getTagWithoutScope = (tag: string) => { return tag.replace(/^(event|instrumentation|link|resource|span)\./, ''); }; diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 0791334f1c1..2f60c94b354 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -36,7 +36,7 @@ import { } from '@grafana/runtime'; import { BarGaugeDisplayMode, TableCellDisplayMode, VariableFormatID } from '@grafana/schema'; -import { generateQueryFromAdHocFilters, getTagWithoutScope, interpolateFilters } from './SearchTraceQLEditor/utils'; +import { getTagWithoutScope, interpolateFilters } from './SearchTraceQLEditor/utils'; import { TempoVariableQuery, TempoVariableQueryType } from './VariableQueryEditor'; import { PrometheusDatasource, PromQuery } from './_importedDependencies/datasources/prometheus/types'; import { TagLimitOptions } from './configuration/TagLimitSettings'; @@ -239,7 +239,7 @@ export class TempoDatasource extends DataSourceWithBackend): Promise> { - const query = generateQueryFromAdHocFilters(options.filters, this.languageProvider); + const query = this.languageProvider.generateQueryFromFilters({ adhocFilters: options.filters }); return this.tagValuesQuery(options.key, query); } @@ -418,7 +418,10 @@ export class TempoDatasource extends DataSourceWithBackend 0) { const appliedQuery = this.applyVariables(traceqlSearchTargets[0], options.scopedVars); - const queryFromFilters = this.languageProvider.generateQueryFromFilters(appliedQuery.filters); + const queryFromFilters = this.languageProvider.generateQueryFromFilters({ + traceqlFilters: appliedQuery.filters, + adhocFilters: options.filters, + }); reportInteraction('grafana_traces_traceql_search_queried', { datasourceType: 'tempo', @@ -918,7 +921,7 @@ export class TempoDatasource extends DataSourceWithBackend { }); it('an empty array', () => { - expect(lp.generateQueryFromFilters([])).toBe('{}'); + expect(lp.generateQueryFromFilters({ traceqlFilters: [] })).toBe('{}'); }); it('a field without value', () => { - expect(lp.generateQueryFromFilters([{ id: 'foo', tag: 'footag', operator: '=' }])).toBe('{}'); + expect(lp.generateQueryFromFilters({ traceqlFilters: [{ id: 'foo', tag: 'footag', operator: '=' }] })).toBe('{}'); }); it('a field with value but without tag', () => { - expect(lp.generateQueryFromFilters([{ id: 'foo', value: 'foovalue', operator: '=' }])).toBe('{}'); + expect(lp.generateQueryFromFilters({ traceqlFilters: [{ id: 'foo', value: 'foovalue', operator: '=' }] })).toBe( + '{}' + ); }); it('a field with value and tag but without operator', () => { - expect(lp.generateQueryFromFilters([{ id: 'foo', tag: 'footag', value: 'foovalue' }])).toBe('{}'); + expect(lp.generateQueryFromFilters({ traceqlFilters: [{ id: 'foo', tag: 'footag', value: 'foovalue' }] })).toBe( + '{}' + ); }); describe('generates correct query for duration when duration type', () => { it('not set', () => { expect( - lp.generateQueryFromFilters([ - { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, + ], + }) ).toBe('{duration>100ms}'); }); it('set to span', () => { expect( - lp.generateQueryFromFilters([ - { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, - { id: 'duration-type', value: 'span' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, + { id: 'duration-type', value: 'span' }, + ], + }) ).toBe('{duration>100ms}'); }); it('set to trace', () => { expect( - lp.generateQueryFromFilters([ - { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, - { id: 'duration-type', value: 'trace' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'min-duration', operator: '>', valueType: 'duration', tag: 'duration', value: '100ms' }, + { id: 'duration-type', value: 'trace' }, + ], + }) ).toBe('{traceDuration>100ms}'); }); }); it('a field with tag, operator and tag', () => { - expect(lp.generateQueryFromFilters([{ id: 'foo', tag: 'footag', value: 'foovalue', operator: '=' }])).toBe( - '{.footag=foovalue}' - ); expect( - lp.generateQueryFromFilters([ - { id: 'foo', tag: 'footag', value: 'foovalue', operator: '=', valueType: 'string' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [{ id: 'foo', tag: 'footag', value: 'foovalue', operator: '=' }], + }) + ).toBe('{.footag=foovalue}'); + expect( + lp.generateQueryFromFilters({ + traceqlFilters: [{ id: 'foo', tag: 'footag', value: 'foovalue', operator: '=', valueType: 'string' }], + }) ).toBe('{.footag="foovalue"}'); }); it('a field with valueType as integer', () => { expect( - lp.generateQueryFromFilters([{ id: 'foo', tag: 'footag', value: '1234', operator: '>', valueType: 'integer' }]) + lp.generateQueryFromFilters({ + traceqlFilters: [{ id: 'foo', tag: 'footag', value: '1234', operator: '>', valueType: 'integer' }], + }) ).toBe('{.footag>1234}'); }); it.each([['=~'], ['!~']])('a field with a regexp operator (%s)', (operator) => { expect( - lp.generateQueryFromFilters([ - { - id: 'span-name', - tag: 'name', - operator, - scope: TraceqlSearchScope.Span, - value: ['api/v2/variants/by-upc/(?P[\\s\\S]*)/$'], - valueType: 'string', - }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { + id: 'span-name', + tag: 'name', + operator, + scope: TraceqlSearchScope.Span, + value: ['api/v2/variants/by-upc/(?P[\\s\\S]*)/$'], + valueType: 'string', + }, + ], + }) ).toBe(`{name${operator}"api/v2/variants/by-upc/\\\\(\\\\?P\\\\[\\\\\\\\s\\\\\\\\S\\\\]\\\\*\\\\)/\\\\$"}`); }); it('two fields with everything filled in', () => { expect( - lp.generateQueryFromFilters([ - { id: 'foo', tag: 'footag', value: '1234', operator: '>=', valueType: 'integer' }, - { id: 'bar', tag: 'bartag', value: 'barvalue', operator: '=', valueType: 'string' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'foo', tag: 'footag', value: '1234', operator: '>=', valueType: 'integer' }, + { id: 'bar', tag: 'bartag', value: 'barvalue', operator: '=', valueType: 'string' }, + ], + }) ).toBe('{.footag>=1234 && .bartag="barvalue"}'); }); it('two fields but one is missing a value', () => { expect( - lp.generateQueryFromFilters([ - { id: 'foo', tag: 'footag', value: '1234', operator: '>=', valueType: 'integer' }, - { id: 'bar', tag: 'bartag', operator: '=', valueType: 'string' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'foo', tag: 'footag', value: '1234', operator: '>=', valueType: 'integer' }, + { id: 'bar', tag: 'bartag', operator: '=', valueType: 'string' }, + ], + }) ).toBe('{.footag>=1234}'); }); it('two fields but one is missing a value and the other a tag', () => { expect( - lp.generateQueryFromFilters([ - { id: 'foo', value: '1234', operator: '>=', valueType: 'integer' }, - { id: 'bar', tag: 'bartag', operator: '=', valueType: 'string' }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { id: 'foo', value: '1234', operator: '>=', valueType: 'integer' }, + { id: 'bar', tag: 'bartag', operator: '=', valueType: 'string' }, + ], + }) ).toBe('{}'); }); it('scope is unscoped', () => { expect( - lp.generateQueryFromFilters([ - { - id: 'foo', - tag: 'footag', - value: '1234', - operator: '>=', - scope: TraceqlSearchScope.Unscoped, - valueType: 'integer', - }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { + id: 'foo', + tag: 'footag', + value: '1234', + operator: '>=', + scope: TraceqlSearchScope.Unscoped, + valueType: 'integer', + }, + ], + }) ).toBe('{.footag>=1234}'); }); it('scope is span', () => { expect( - lp.generateQueryFromFilters([ - { - id: 'foo', - tag: 'footag', - value: '1234', - operator: '>=', - scope: TraceqlSearchScope.Span, - valueType: 'integer', - }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { + id: 'foo', + tag: 'footag', + value: '1234', + operator: '>=', + scope: TraceqlSearchScope.Span, + valueType: 'integer', + }, + ], + }) ).toBe('{span.footag>=1234}'); }); it('scope is resource', () => { expect( - lp.generateQueryFromFilters([ - { - id: 'foo', - tag: 'footag', - value: '1234', - operator: '>=', - scope: TraceqlSearchScope.Resource, - valueType: 'integer', - }, - ]) + lp.generateQueryFromFilters({ + traceqlFilters: [ + { + id: 'foo', + tag: 'footag', + value: '1234', + operator: '>=', + scope: TraceqlSearchScope.Resource, + valueType: 'integer', + }, + ], + }) ).toBe('{resource.footag>=1234}'); }); + describe('adhoc filters', () => { + it('mixes adhoc filters with trace ql', () => { + expect( + lp.generateQueryFromFilters({ + traceqlFilters: [ + { + id: 'foo', + tag: 'footag', + value: '1234', + operator: '>=', + scope: TraceqlSearchScope.Resource, + valueType: 'integer', + }, + ], + adhocFilters: [ + { + key: 'resource.name', + operator: '=', + value: 'foo', + }, + ], + }) + ).toBe('{resource.footag>=1234 && resource.name="foo"}'); + }); + it('an empty array', () => { + expect(lp.generateQueryFromFilters({ adhocFilters: [] })).toBe('{}'); + }); + + it('a filter with values', () => { + expect( + lp.generateQueryFromFilters({ adhocFilters: [{ key: 'footag', operator: '=', value: 'foovalue' }] }) + ).toBe('{footag="foovalue"}'); + }); + + it('two filters with values', () => { + expect( + lp.generateQueryFromFilters({ + adhocFilters: [ + { key: 'footag', operator: '=', value: 'foovalue' }, + { key: 'bartag', operator: '=', value: '0' }, + ], + }) + ).toBe('{footag="foovalue" && bartag=0}'); + }); + + it('a filter with enum intrinsic values', () => { + expect(lp.generateQueryFromFilters({ adhocFilters: [{ key: 'kind', operator: '=', value: 'server' }] })).toBe( + '{kind=server}' + ); + }); + + it('a filter with non-enum intrinsic values', () => { + expect( + lp.generateQueryFromFilters({ adhocFilters: [{ key: 'name', operator: '=', value: 'my-server' }] }) + ).toBe('{name="my-server"}'); + }); + }); }); const setup = (tagsV1?: string[], tagsV2?: Scope[]) => { diff --git a/public/app/plugins/datasource/tempo/language_provider.ts b/public/app/plugins/datasource/tempo/language_provider.ts index e8c4d202dde..6aa67bb750e 100644 --- a/public/app/plugins/datasource/tempo/language_provider.ts +++ b/public/app/plugins/datasource/tempo/language_provider.ts @@ -1,4 +1,4 @@ -import { LanguageProvider, SelectableValue } from '@grafana/data'; +import { AdHocVariableFilter, LanguageProvider, SelectableValue } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { VariableFormatID } from '@grafana/schema'; @@ -11,7 +11,7 @@ import { } from './SearchTraceQLEditor/utils'; import { TraceqlFilter, TraceqlSearchScope } from './dataquery.gen'; import { TempoDatasource } from './datasource'; -import { intrinsicsV1 } from './traceql/traceql'; +import { enumIntrinsics, intrinsicsV1 } from './traceql/traceql'; import { Scope } from './types'; // Limit maximum tags retrieved from the backend @@ -179,14 +179,48 @@ export default class TempoLanguageProvider extends LanguageProvider { return encodeURIComponent(encodeURIComponent(tag)); }; - generateQueryFromFilters(filters: TraceqlFilter[]) { + generateQueryFromFilters({ + traceqlFilters, + adhocFilters, + }: { + traceqlFilters?: TraceqlFilter[]; + adhocFilters?: AdHocVariableFilter[]; + }) { + if (!traceqlFilters && !adhocFilters) { + return ''; + } + + const allFilters = [ + ...this.generateQueryFromTraceQlFilters(traceqlFilters || []), + ...this.generateQueryFromAdHocFilters(adhocFilters || []), + ]; + + return `{${allFilters.join(' && ')}}`; + } + + private generateQueryFromTraceQlFilters(filters: TraceqlFilter[]) { if (!filters) { return ''; } - return `{${filters + return filters .filter((f) => f.tag && f.operator && f.value?.length) - .map((f) => filterToQuerySection(f, filters, this)) - .join(' && ')}}`; + .map((f) => filterToQuerySection(f, filters, this)); } + + private generateQueryFromAdHocFilters = (filters: AdHocVariableFilter[]) => { + return filters + .filter((f) => f.key && f.operator && f.value) + .map((f) => `${f.key}${f.operator}${this.adHocValueHelper(f)}`); + }; + + adHocValueHelper = (f: AdHocVariableFilter) => { + if (this.getIntrinsics().find((t) => t === f.key) && enumIntrinsics.includes(f.key)) { + return f.value; + } + if (parseInt(f.value, 10).toString() === f.value) { + return f.value; + } + return `"${f.value}"`; + }; } diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index c4c90c78041..f80f9986f93 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -22,7 +22,9 @@ export function QueryEditor(props: Props) { const styles = useStyles2(getStyles); const query = defaults(props.query, defaultQuery); const [showCopyFromSearchButton, setShowCopyFromSearchButton] = useState(() => { - const genQuery = props.datasource.languageProvider.generateQueryFromFilters(query.filters || []); + const genQuery = props.datasource.languageProvider.generateQueryFromFilters({ + traceqlFilters: query.filters || [], + }); return genQuery === query.query || genQuery === '{}'; }); @@ -50,7 +52,9 @@ export function QueryEditor(props: Props) { props.onClearResults(); props.onChange({ ...query, - query: props.datasource.languageProvider.generateQueryFromFilters(query.filters || []), + query: props.datasource.languageProvider.generateQueryFromFilters({ + traceqlFilters: query.filters || [], + }), }); setShowCopyFromSearchButton(true); }} diff --git a/public/app/plugins/datasource/tempo/traceql/traceql.ts b/public/app/plugins/datasource/tempo/traceql/traceql.ts index 1dea15994dd..00f54a0d25f 100644 --- a/public/app/plugins/datasource/tempo/traceql/traceql.ts +++ b/public/app/plugins/datasource/tempo/traceql/traceql.ts @@ -58,6 +58,8 @@ export const intrinsics = intrinsicsV1.concat([ ]); export const scopes: string[] = ['event', 'instrumentation', 'link', 'resource', 'span']; +export const enumIntrinsics = ['kind', 'span:kind', 'status', 'span:status']; + const aggregatorFunctions = ['avg', 'count', 'max', 'min', 'sum']; const functions = aggregatorFunctions.concat([ 'by', From b4758d06a3f47884373aa0c703b172ee576955f5 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Mon, 24 Mar 2025 14:15:25 +0100 Subject: [PATCH 31/86] Alerting: Support JSON Content-Type in the Prometheus conversion API (#102627) Alerting: Support content-type: json in conversion API --- pkg/apimachinery/errutil/errors.go | 11 +++ pkg/apimachinery/errutil/status.go | 8 ++ .../ngalert/api/prometheus_conversion.go | 31 +++++++- .../alerting/api_convert_prometheus_test.go | 73 +++++++++++++++---- pkg/tests/api/alerting/testing.go | 18 ++++- 5 files changed, 122 insertions(+), 19 deletions(-) diff --git a/pkg/apimachinery/errutil/errors.go b/pkg/apimachinery/errutil/errors.go index e8bc0e4b0f2..2bb0fb79c73 100644 --- a/pkg/apimachinery/errutil/errors.go +++ b/pkg/apimachinery/errutil/errors.go @@ -71,6 +71,17 @@ func UnprocessableEntity(msgID string, opts ...BaseOpt) Base { return NewBase(StatusUnprocessableEntity, msgID, opts...) } +// UnsupportedMediaType initializes a new [Base] error with reason StatusUnsupportedMediaType +// that is used to construct [Error]. The msgID is passed to the caller +// to serve as the base for user facing error messages. +// +// msgID should be structured as component.errorBrief, for example +// +// plugin.unsupportedMediaType +func UnsupportedMediaType(msgID string, opts ...BaseOpt) Base { + return NewBase(StatusUnsupportedMediaType, msgID, opts...) +} + // Conflict initializes a new [Base] error with reason StatusConflict // that is used to construct [Error]. The msgID is passed to the caller // to serve as the base for user facing error messages. diff --git a/pkg/apimachinery/errutil/status.go b/pkg/apimachinery/errutil/status.go index 6379dcdf447..edfede773ba 100644 --- a/pkg/apimachinery/errutil/status.go +++ b/pkg/apimachinery/errutil/status.go @@ -29,6 +29,10 @@ const ( // contained instructions. // HTTP status code 422. StatusUnprocessableEntity CoreStatus = "Unprocessable Entity" + // StatusUnsupportedMediaType means that the server does not support + // the request payload's media type. + // HTTP status code 415. + StatusUnsupportedMediaType CoreStatus = CoreStatus(metav1.StatusReasonUnsupportedMediaType) // StatusConflict means that the server cannot fulfill the request // there is a conflict in the current state of a resource // HTTP status code 409. @@ -107,6 +111,8 @@ func (s CoreStatus) HTTPStatus() int { return http.StatusGatewayTimeout case StatusUnprocessableEntity: return http.StatusUnprocessableEntity + case StatusUnsupportedMediaType: + return http.StatusUnsupportedMediaType case StatusConflict: return http.StatusConflict case StatusTooManyRequests: @@ -137,6 +143,8 @@ func (s CoreStatus) LogLevel() LogLevel { return LevelInfo case StatusTimeout: return LevelInfo + case StatusUnsupportedMediaType: + return LevelInfo case StatusUnprocessableEntity: return LevelInfo case StatusConflict: diff --git a/pkg/services/ngalert/api/prometheus_conversion.go b/pkg/services/ngalert/api/prometheus_conversion.go index d48aa5444b9..9862c862b9a 100644 --- a/pkg/services/ngalert/api/prometheus_conversion.go +++ b/pkg/services/ngalert/api/prometheus_conversion.go @@ -1,15 +1,20 @@ package api import ( + "encoding/json" "io" + "mime" "gopkg.in/yaml.v3" "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/apimachinery/errutil" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ) +var errorUnsupportedMediaType = errutil.UnsupportedMediaType("alerting.unsupportedMediaType") + type ConvertPrometheusApiHandler struct { svc *ConvertPrometheusSrv } @@ -49,8 +54,30 @@ func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusPostRuleGroup( defer func() { _ = ctx.Req.Body.Close() }() var promGroup apimodels.PrometheusRuleGroup - if err := yaml.Unmarshal(body, &promGroup); err != nil { - return errorToResponse(err) + var m string + + // Parse content-type only if it's not empty, + // otherwise we'll assume it's yaml + contentType := ctx.Req.Header.Get("content-type") + if contentType != "" { + m, _, err = mime.ParseMediaType(contentType) + if err != nil { + return errorToResponse(err) + } + } + + switch m { + case "application/yaml", "": + // mimirtool does not send content-type, so if it's empty, we assume it's yaml + if err := yaml.Unmarshal(body, &promGroup); err != nil { + return errorToResponse(err) + } + case "application/json": + if err := json.Unmarshal(body, &promGroup); err != nil { + return errorToResponse(err) + } + default: + return errorToResponse(errorUnsupportedMediaType.Errorf("unsupported media type: %s, only application/yaml and application/json are supported", m)) } return f.svc.RouteConvertPrometheusPostRuleGroup(ctx, namespaceTitle, promGroup) diff --git a/pkg/tests/api/alerting/api_convert_prometheus_test.go b/pkg/tests/api/alerting/api_convert_prometheus_test.go index e07382e6bca..df25c2ac8f5 100644 --- a/pkg/tests/api/alerting/api_convert_prometheus_test.go +++ b/pkg/tests/api/alerting/api_convert_prometheus_test.go @@ -100,7 +100,7 @@ var ( ) func TestIntegrationConvertPrometheusEndpoints(t *testing.T) { - runTest := func(t *testing.T, enableLokiPaths bool) { + runTest := func(t *testing.T, enableLokiPaths bool, postContentType string) { testinfra.SQLiteIntegrationTest(t) // Setup Grafana and its Database @@ -136,12 +136,16 @@ func TestIntegrationConvertPrometheusEndpoints(t *testing.T) { ds := apiClient.CreateDatasource(t, datasources.DS_PROMETHEUS) + postContentTypeHeader := map[string]string{ + "Content-Type": postContentType, + } + t.Run("create rule groups and get them back", func(t *testing.T) { - apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup1, nil) - apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup2, nil) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup1, postContentTypeHeader) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup2, postContentTypeHeader) // create a third group in a different namespace - apiClient.ConvertPrometheusPostRuleGroup(t, namespace2, ds.Body.Datasource.UID, promGroup3, nil) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace2, ds.Body.Datasource.UID, promGroup3, postContentTypeHeader) // And a non-provisioned rule in another namespace namespace3UID := util.GenerateShortUID() @@ -173,11 +177,16 @@ func TestIntegrationConvertPrometheusEndpoints(t *testing.T) { requireStatusCode(t, http.StatusForbidden, status, raw) }) + t.Run("with incorrect content-type should receive 415", func(t *testing.T) { + _, status, raw := apiClient.RawConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup1, map[string]string{"Content-Type": "application/xml"}) + requireStatusCode(t, http.StatusUnsupportedMediaType, status, raw) + }) + t.Run("delete one rule group", func(t *testing.T) { // Create three groups - apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup1, nil) - apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup2, nil) - apiClient.ConvertPrometheusPostRuleGroup(t, namespace2, ds.Body.Datasource.UID, promGroup3, nil) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup1, postContentTypeHeader) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace1, ds.Body.Datasource.UID, promGroup2, postContentTypeHeader) + apiClient.ConvertPrometheusPostRuleGroup(t, namespace2, ds.Body.Datasource.UID, promGroup3, postContentTypeHeader) // delete the first one apiClient.ConvertPrometheusDeleteRuleGroup(t, namespace1, promGroup1.Name, nil) @@ -202,13 +211,51 @@ func TestIntegrationConvertPrometheusEndpoints(t *testing.T) { }) } - t.Run("with the mimirtool paths", func(t *testing.T) { - runTest(t, false) - }) + const applicationYAML = "application/yaml" + const applicationJSON = "application/json" + + cases := []struct { + name string + contentType string + enableLokiPaths bool + }{ + { + name: "with the mimirtool paths; empty content-type", + contentType: "", + enableLokiPaths: false, + }, + { + name: "with the cortextool Loki paths; empty content-type", + contentType: "", + enableLokiPaths: true, + }, + { + name: "with the mimirtool paths; yaml", + contentType: applicationYAML, + enableLokiPaths: false, + }, + { + name: "with the cortextool Loki paths; yaml", + contentType: applicationYAML, + enableLokiPaths: true, + }, + { + name: "with the mimirtool paths; json", + contentType: applicationJSON, + enableLokiPaths: false, + }, + { + name: "with the cortextool Loki paths; json", + contentType: applicationJSON, + enableLokiPaths: true, + }, + } - t.Run("with the cortextool Loki paths", func(t *testing.T) { - runTest(t, true) - }) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + runTest(t, tc.enableLokiPaths, tc.contentType) + }) + } } func TestIntegrationConvertPrometheusEndpoints_UpdateRule(t *testing.T) { diff --git a/pkg/tests/api/alerting/testing.go b/pkg/tests/api/alerting/testing.go index 429f32e755e..f9bb02df433 100644 --- a/pkg/tests/api/alerting/testing.go +++ b/pkg/tests/api/alerting/testing.go @@ -1145,16 +1145,26 @@ func (a apiClient) RawConvertPrometheusPostRuleGroup(t *testing.T, namespaceTitl path = "%s/api/convert/api/prom/rules/%s" } - data, err := yaml.Marshal(promGroup) - require.NoError(t, err) + // Based on the content-type header, marshal the data to JSON or YAML + contentType := headers["Content-Type"] + var data []byte + var err error + if contentType == "application/json" { + data, err = json.Marshal(promGroup) + require.NoError(t, err) + } else { + data, err = yaml.Marshal(promGroup) + require.NoError(t, err) + } + buf := bytes.NewReader(data) req, err := http.NewRequest(http.MethodPost, fmt.Sprintf(path, a.url, namespaceTitle), buf) require.NoError(t, err) - req.Header.Add("X-Grafana-Alerting-Datasource-UID", datasourceUID) + req.Header.Set("X-Grafana-Alerting-Datasource-UID", datasourceUID) for key, value := range headers { - req.Header.Add(key, value) + req.Header.Set(key, value) } return sendRequestJSON[apimodels.ConvertPrometheusResponse](t, req, http.StatusAccepted) From 030893ac23bf508cde3d9590645a53af0822eabf Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 24 Mar 2025 13:50:50 +0000 Subject: [PATCH 32/86] ScrollIndicators: Tweak to be more subtle (#102539) * tweak scroll indicators to be more subtle * remove extra bracket --- .../src/components/ScrollContainer/ScrollIndicators.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ScrollContainer/ScrollIndicators.tsx b/packages/grafana-ui/src/components/ScrollContainer/ScrollIndicators.tsx index 8d7819e7efe..2263b607db1 100644 --- a/packages/grafana-ui/src/components/ScrollContainer/ScrollIndicators.tsx +++ b/packages/grafana-ui/src/components/ScrollContainer/ScrollIndicators.tsx @@ -54,6 +54,9 @@ export const ScrollIndicators = ({ children }: React.PropsWithChildren<{}>) => { }; const getStyles = (theme: GrafanaTheme2) => { + // we specifically don't want a theme color here + // this gradient is more like a shadow + const scrollGradientColor = `rgba(0, 0, 0, ${theme.isDark ? 0.25 : 0.08})`; return { scrollContent: css({ display: 'flex', @@ -62,7 +65,7 @@ const getStyles = (theme: GrafanaTheme2) => { position: 'relative', }), scrollIndicator: css({ - height: theme.spacing(6), + height: `max(5%, ${theme.spacing(3)})`, left: 0, opacity: 0, pointerEvents: 'none', @@ -74,11 +77,11 @@ const getStyles = (theme: GrafanaTheme2) => { zIndex: 1, }), scrollTopIndicator: css({ - background: `linear-gradient(0deg, transparent, ${theme.colors.background.canvas})`, + background: `linear-gradient(0deg, transparent, ${scrollGradientColor})`, top: 0, }), scrollBottomIndicator: css({ - background: `linear-gradient(180deg, transparent, ${theme.colors.background.canvas})`, + background: `linear-gradient(180deg, transparent, ${scrollGradientColor})`, bottom: 0, }), scrollIndicatorVisible: css({ From b0fda33d06c6f64ee9ee482c86f5a2160c4b1eaa Mon Sep 17 00:00:00 2001 From: Jacob Valdez Date: Mon, 24 Mar 2025 08:58:32 -0500 Subject: [PATCH 33/86] Docs: What's new & Upgrade guide v11.6 (#101479) * Docs: What's new & Upgrade guide v11.6 * Adding alerting entries * adding new entries * updating entries and initial text from website PR * Reorder changelog entries * adjusting front matter spacing for product labels * adding missing space --------- Co-authored-by: Mitch Seaman Co-authored-by: Jack Baldry --- .../upgrade-guide/upgrade-v11.6/index.md | 22 ++++++++ docs/sources/whatsnew/_index.md | 1 + docs/sources/whatsnew/whats-new-in-v11-6.md | 50 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 docs/sources/upgrade-guide/upgrade-v11.6/index.md create mode 100644 docs/sources/whatsnew/whats-new-in-v11-6.md diff --git a/docs/sources/upgrade-guide/upgrade-v11.6/index.md b/docs/sources/upgrade-guide/upgrade-v11.6/index.md new file mode 100644 index 00000000000..1a1e05ad4df --- /dev/null +++ b/docs/sources/upgrade-guide/upgrade-v11.6/index.md @@ -0,0 +1,22 @@ +--- +description: Guide for upgrading to Grafana v11.6 +keywords: + - grafana + - configuration + - documentation + - upgrade + - '11.6' +title: Upgrade to Grafana v11.6 +menuTitle: Upgrade to v11.6 +weight: 600 +--- + +# Upgrade to Grafana v11.6 + +{{< docs/shared lookup="upgrade/intro.md" source="grafana" version="" >}} + +{{< docs/shared lookup="back-up/back-up-grafana.md" source="grafana" version="" leveloffset="+1" >}} + +{{< docs/shared lookup="upgrade/upgrade-common-tasks.md" source="grafana" version="" >}} + +## Technical notes diff --git a/docs/sources/whatsnew/_index.md b/docs/sources/whatsnew/_index.md index dfe2b307d4d..5927368bb6b 100644 --- a/docs/sources/whatsnew/_index.md +++ b/docs/sources/whatsnew/_index.md @@ -76,6 +76,7 @@ For a complete list of every change, with links to pull requests and related iss ## Grafana 11 +- [What's new in 11.6](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-6) - [What's new in 11.5](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-5/) - [What's new in 11.4](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-4/) - [What's new in 11.3](https://grafana.com/docs/grafana//whatsnew/whats-new-in-v11-3/) diff --git a/docs/sources/whatsnew/whats-new-in-v11-6.md b/docs/sources/whatsnew/whats-new-in-v11-6.md new file mode 100644 index 00000000000..c4d95598192 --- /dev/null +++ b/docs/sources/whatsnew/whats-new-in-v11-6.md @@ -0,0 +1,50 @@ +--- +description: Feature and improvement highlights for Grafana v11.6 +keywords: + - grafana + - new + - documentation + - '11.6' + - release notes +labels: + products: + - cloud + - enterprise + - oss +title: What's new in Grafana v11.6 +posts: + - title: Dashboards and visualizations + items: + - docs/grafana-cloud/whats-new/2025-02-11-canvas-one-click-data-links-and-actions.md + - docs/grafana-cloud/whats-new/2025-02-11-one-click-data-links-and-actions-in-visualizations.md + - docs/grafana-cloud/whats-new/2025-02-14-actions-added-to-visualizations.md + - docs/grafana-cloud/whats-new/2025-02-26-new-actionscell-for-table-visualization.md + - docs/grafana-cloud/whats-new/2025-02-19-better-time-region-control-with-cron-syntax.md + - docs/grafana-cloud/whats-new/2025-03-06-improved-performance-in-geomap-visualizations.md + - docs/grafana-cloud/whats-new/2025-03-06-variables-supported-for-all-transformations.md + - title: Alerting + items: + - docs/grafana-cloud/whats-new/2025-03-05-alert-rule-version-history.md + - docs/grafana-cloud/whats-new/2025-03-05-alerting-support-for-jira-service-management-contact-point.md + - title: Data sources + items: + - docs/grafana-cloud/whats-new/2025-02-28-lbac-for-datasources-metrics.md + - title: Plugins + items: + - docs/grafana-cloud/whats-new/2025-03-12-plugin-details-links-improvements.md + - title: Security + items: + - docs/grafana-cloud/whats-new/2025-02-10-auto-migration-of-api-keys-to-service-accounts.md +whats_new_grafana_version: 11.6 +weight: -48 +--- + +# What’s new in Grafana v11.6 + +Welcome to Grafana 11.6! This minor release includes a number of dashboarding features that are now generally available including one-click data links and actions, Cron syntax support for annotations, and WebGL-powered geomaps for better performance. We've also fully migrated from API keys to service accounts in Grafana for better security. Read on to learn more about version history for Grafana Managed Alerts, label-based access control (LBAC) for Mimir metrics, and more in Grafana v11.6. + +{{< youtube id=iF7yxO4nUXQ >}} + +For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). For the specific steps we recommend when you upgrade to v11.6, check out our [Upgrade Guide](https://grafana.com/docs/grafana//upgrade-guide/upgrade-v11.6/). + +{{< docs/whats-new >}} From dcaa40312ff5e88519efc7522f7dcb32b4c81984 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Mon, 24 Mar 2025 15:14:41 +0100 Subject: [PATCH 34/86] Alerting: Fix typo in the recording_rules ini section (#102707) --- conf/defaults.ini | 2 +- conf/sample.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index a4679a13a6f..4fc429d33a1 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1544,7 +1544,7 @@ basic_auth_password = timeout = 10s # Default data source UID to write to if not specified in the rule definition. -# Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled. +# Only has effect if the grafanaManagedRecordingRulesDatasources feature toggle is enabled. default_datasource_uid = # Optional custom headers to include in recording rule write requests. diff --git a/conf/sample.ini b/conf/sample.ini index f06e41efed9..b170dfceef2 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1526,7 +1526,7 @@ basic_auth_password = timeout = 30s # Default data source UID to write to if not specified in the rule definition. -# Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled. +# Only has effect if the grafanaManagedRecordingRulesDatasources feature toggle is enabled. default_datasource_uid = # Optional custom headers to include in recording rule write requests. From e862fb3cd78a1b49a6f4f7243d165b46c4f83668 Mon Sep 17 00:00:00 2001 From: Mariell Hoversholm Date: Mon, 24 Mar 2025 15:22:53 +0100 Subject: [PATCH 35/86] Security: Update JWT library (CVE-2025-30204) (#102715) * Security: Update JWT library * Security: Update JWT v5 library --- apps/alerting/notifications/go.sum | 4 ++-- go.mod | 4 ++-- go.sum | 8 ++++---- go.work.sum | 4 ++-- pkg/aggregator/go.mod | 2 +- pkg/aggregator/go.sum | 4 ++-- pkg/apis/secret/go.mod | 2 +- pkg/apis/secret/go.sum | 4 ++-- pkg/apiserver/go.mod | 2 +- pkg/apiserver/go.sum | 4 ++-- pkg/promlib/go.mod | 2 ++ pkg/promlib/go.sum | 8 ++++---- pkg/storage/unified/apistore/go.mod | 4 ++-- pkg/storage/unified/apistore/go.sum | 8 ++++---- pkg/storage/unified/resource/go.mod | 2 +- pkg/storage/unified/resource/go.sum | 8 ++++---- 16 files changed, 36 insertions(+), 34 deletions(-) diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index f613d6fe414..bcec2810d71 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -44,8 +44,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/go.mod b/go.mod index 6be820fd92c..9e0d22a8f6e 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend - github.com/golang-jwt/jwt/v4 v4.5.1 // @grafana/grafana-backend-group + github.com/golang-jwt/jwt/v4 v4.5.2 // @grafana/grafana-backend-group github.com/golang-migrate/migrate/v4 v4.7.0 // @grafana/grafana-backend-group github.com/golang/mock v1.7.0-rc.1 // @grafana/alerting-backend github.com/golang/protobuf v1.5.4 // @grafana/grafana-backend-group @@ -363,7 +363,7 @@ require ( github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect diff --git a/go.sum b/go.sum index 3e27d6a9642..27e7aab477f 100644 --- a/go.sum +++ b/go.sum @@ -1364,10 +1364,10 @@ github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= diff --git a/go.work.sum b/go.work.sum index 9c6ea83ad05..91bbc83d04a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -936,6 +936,8 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E= @@ -990,8 +992,6 @@ github.com/grafana/cog v0.0.23 h1:/0CCJ24Z8XXM2DnboSd2FzoIswUroqIZzVr8oJWmMQs= github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= github.com/grafana/grafana-plugin-sdk-go v0.266.0/go.mod h1:bxkXrBQ4QSmOncsWdIOcpgP+M6wajQNMAPXlbWrqAWY= github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index eb227a8af37..bb8cda1c510 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -51,7 +51,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.23.2 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index e947b008d44..76a80eb2849 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -98,8 +98,8 @@ github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/pkg/apis/secret/go.mod b/pkg/apis/secret/go.mod index 4c61171ac6c..521a4f43fb1 100644 --- a/pkg/apis/secret/go.mod +++ b/pkg/apis/secret/go.mod @@ -30,7 +30,7 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect diff --git a/pkg/apis/secret/go.sum b/pkg/apis/secret/go.sum index 0f270b903fb..e08fdd84fcd 100644 --- a/pkg/apis/secret/go.sum +++ b/pkg/apis/secret/go.sum @@ -53,8 +53,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 39a9e18756d..11ec613e724 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -37,7 +37,7 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 1d54379d869..7a26f70eafe 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -53,8 +53,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 020113b5982..64fad347ab6 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -21,6 +21,7 @@ require ( github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/aws/aws-sdk-go v1.55.6 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -43,6 +44,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/gnostic-models v0.6.8 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index cbc5e25726a..a5590b37cb3 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -26,8 +26,8 @@ github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -90,8 +90,8 @@ github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 861b134152f..7955d73ee7d 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -182,8 +182,8 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-migrate/migrate/v4 v4.7.0 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index c1c61c2582c..bcc6edb5699 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -1087,10 +1087,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 354a247aee9..3877ed0daa3 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -117,7 +117,7 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-migrate/migrate/v4 v4.7.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 62654cc32e4..0f72e759646 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -994,10 +994,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= From bef5df2476153d481bf1ac6b6958c3ca8f731090 Mon Sep 17 00:00:00 2001 From: Alex Bikfalvi Date: Mon, 24 Mar 2025 10:24:10 -0400 Subject: [PATCH 36/86] feat(datasource/Tempo): Report timerange to Rudderstack (#102218) * feat(datasource/Tempo): Report timerange to Rudderstack This adds another parameter to the Rudderstack instrumentation, reporting the query time range in seconds. Signed-off-by: Alex Bikfalvi * Update public/app/plugins/datasource/tempo/datasource.ts Co-authored-by: Andre Pereira * Update public/app/plugins/datasource/tempo/datasource.ts --------- Signed-off-by: Alex Bikfalvi Co-authored-by: Andre Pereira --- public/app/plugins/datasource/tempo/datasource.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 2f60c94b354..5b91b5f25e9 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -1529,6 +1529,8 @@ function reportTempoQueryMetrics( datasourceType: 'tempo', app: options.app ?? '', grafana_version: config.buildInfo.version, + timeRangeSeconds: options.range ? options.range.to.unix() - options.range.from.unix() : 0, + timeRange: options.range ? options.range.raw.from + ';' + options.range.raw.to : '', ...metrics, }); } From 55f28124665e73f0ced273f854fe1eabfe225a8c Mon Sep 17 00:00:00 2001 From: Karl Persson <23356117+kalleep@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:00:07 +0100 Subject: [PATCH 37/86] Annotations: Fix annotations scope resolver (#102612) * Fix annotations scope resolver --- pkg/api/annotations.go | 30 ++++++++++++++++-------------- pkg/api/annotations_test.go | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go index 5873504d7c9..d0e56d1c052 100644 --- a/pkg/api/annotations.go +++ b/pkg/api/annotations.go @@ -633,23 +633,25 @@ func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, feature if annotation.DashboardID == 0 { return []string{accesscontrol.ScopeAnnotationsTypeOrganization}, nil } else { - dashboard, err := dashSvc.GetDashboard(ctx, &dashboards.GetDashboardQuery{ID: annotation.DashboardID, OrgID: orgID}) - if err != nil { - return nil, err - } - scopes := []string{dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)} - // Append dashboard parent scopes if dashboard is in a folder or the general scope if dashboard is not in a folder - if dashboard.FolderUID != "" { - scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(dashboard.FolderUID)) - inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, dashboard.FolderUID, folderSvc) + return identity.WithServiceIdentityFn(ctx, orgID, func(ctx context.Context) ([]string, error) { + dashboard, err := dashSvc.GetDashboard(ctx, &dashboards.GetDashboardQuery{ID: annotation.DashboardID, OrgID: orgID}) if err != nil { return nil, err } - scopes = append(scopes, inheritedScopes...) - } else { - scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.GeneralFolderUID)) - } - return scopes, nil + scopes := []string{dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)} + // Append dashboard parent scopes if dashboard is in a folder or the general scope if dashboard is not in a folder + if dashboard.FolderUID != "" { + scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(dashboard.FolderUID)) + inheritedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, dashboard.FolderUID, folderSvc) + if err != nil { + return nil, err + } + scopes = append(scopes, inheritedScopes...) + } else { + scopes = append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.GeneralFolderUID)) + } + return scopes, nil + }) } }) } diff --git a/pkg/api/annotations_test.go b/pkg/api/annotations_test.go index fa4f119f13b..6eaf4317be6 100644 --- a/pkg/api/annotations_test.go +++ b/pkg/api/annotations_test.go @@ -436,8 +436,8 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) { dashSvc := &dashboards.FakeDashboardService{} rootDash := &dashboards.Dashboard{ID: 1, OrgID: 1, UID: rootDashUID} folderDash := &dashboards.Dashboard{ID: 2, OrgID: 1, UID: folderDashUID, FolderUID: folderUID} - dashSvc.On("GetDashboard", context.Background(), &dashboards.GetDashboardQuery{ID: rootDash.ID, OrgID: 1}).Return(rootDash, nil) - dashSvc.On("GetDashboard", context.Background(), &dashboards.GetDashboardQuery{ID: folderDash.ID, OrgID: 1}).Return(folderDash, nil) + dashSvc.On("GetDashboard", mock.Anything, &dashboards.GetDashboardQuery{ID: rootDash.ID, OrgID: 1}).Return(rootDash, nil) + dashSvc.On("GetDashboard", mock.Anything, &dashboards.GetDashboardQuery{ID: folderDash.ID, OrgID: 1}).Return(folderDash, nil) rootDashboardAnnotation := annotations.Item{ID: 1, DashboardID: rootDash.ID} folderDashboardAnnotation := annotations.Item{ID: 3, DashboardID: folderDash.ID} From c76a681a43151024c49ed38ed50a0ce762c4f2b8 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Mon, 24 Mar 2025 09:48:46 -0600 Subject: [PATCH 38/86] Dashboards: Prevent version restore to same data (#102665) --- .../developers/http_api/dashboard_versions.md | 1 + pkg/api/dashboard.go | 20 ++++++ pkg/api/dashboard_test.go | 72 ++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/docs/sources/developers/http_api/dashboard_versions.md b/docs/sources/developers/http_api/dashboard_versions.md index 96960701098..ef25424bbc7 100644 --- a/docs/sources/developers/http_api/dashboard_versions.md +++ b/docs/sources/developers/http_api/dashboard_versions.md @@ -216,6 +216,7 @@ JSON response body schema: Status codes: - **200** - OK +- **400** - Bad request (specified version has the same content as the current dashboard) - **401** - Unauthorized - **404** - Not found (dashboard not found or dashboard version not found) - **500** - Internal server error (indicates issue retrieving dashboard tags from database) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 17d37830645..4406b5d8891 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path/filepath" + "reflect" "strconv" "strings" @@ -1114,6 +1115,13 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) respon return response.Error(http.StatusNotFound, "Dashboard version not found", nil) } + // do not allow restores if the json data is identical + // this is needed for the k8s flow, as the generation id will be used on the + // version table, and the generation id only increments when the actual spec is changed + if compareDashboardData(version.Data.MustMap(), dash.Data.MustMap()) { + return response.Error(http.StatusBadRequest, "Current dashboard is identical to the specified version", nil) + } + var userID int64 if id, err := identity.UserIdentifier(c.SignedInUser.GetID()); err == nil { userID = id @@ -1135,6 +1143,18 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) respon return hs.postDashboard(c, saveCmd) } +func compareDashboardData(versionData, dashData map[string]any) bool { + // these can be different but the actual data is the same + delete(versionData, "version") + delete(dashData, "version") + delete(versionData, "id") + delete(dashData, "id") + delete(versionData, "uid") + delete(dashData, "uid") + + return reflect.DeepEqual(versionData, dashData) +} + // swagger:route GET /dashboards/tags dashboards getDashboardTags // // Get all dashboards tags of an organisation. diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 0dfdcd43934..c32c3d1835c 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -585,6 +585,38 @@ func TestDashboardAPIEndpoint(t *testing.T) { } }).Return(nil, nil) + cmd := dtos.RestoreDashboardVersionCommand{ + Version: 1, + } + fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake() + fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{ + { + DashboardID: 2, + Version: 1, + Data: simplejson.NewFromAny(map[string]any{ + "title": "Dash1", + }), + }, + } + mockSQLStore := dbtest.NewFakeDB() + + restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore", + "/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) { + sc.dashboardVersionService = fakeDashboardVersionService + + callRestoreDashboardVersion(sc) + assert.Equal(t, http.StatusOK, sc.resp.Code) + }, mockSQLStore) + }) + + t.Run("Should not be able to restore to the same data", func(t *testing.T) { + fakeDash := dashboards.NewDashboard("Child dash") + fakeDash.ID = 2 + fakeDash.HasACL = false + + dashboardService := dashboards.NewFakeDashboardService(t) + dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(fakeDash, nil) + cmd := dtos.RestoreDashboardVersionCommand{ Version: 1, } @@ -602,6 +634,42 @@ func TestDashboardAPIEndpoint(t *testing.T) { "/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) { sc.dashboardVersionService = fakeDashboardVersionService + callRestoreDashboardVersion(sc) + assert.Equal(t, http.StatusBadRequest, sc.resp.Code) + }, mockSQLStore) + }) + + t.Run("Given dashboard in general folder being restored should restore to general folder", func(t *testing.T) { + fakeDash := dashboards.NewDashboard("Child dash") + fakeDash.ID = 2 + fakeDash.HasACL = false + + dashboardService := dashboards.NewFakeDashboardService(t) + dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(fakeDash, nil) + dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).Run(func(args mock.Arguments) { + cmd := args.Get(1).(*dashboards.SaveDashboardDTO) + cmd.Dashboard = &dashboards.Dashboard{ + ID: 2, UID: "uid", Title: "Dash", Slug: "dash", Version: 1, + } + }).Return(nil, nil) + + fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake() + fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{ + { + DashboardID: 2, + Version: 1, + Data: simplejson.NewFromAny(map[string]any{ + "title": "Dash1", + }), + }, + } + + cmd := dtos.RestoreDashboardVersionCommand{ + Version: 1, + } + mockSQLStore := dbtest.NewFakeDB() + restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore", + "/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) { callRestoreDashboardVersion(sc) assert.Equal(t, http.StatusOK, sc.resp.Code) }, mockSQLStore) @@ -626,7 +694,9 @@ func TestDashboardAPIEndpoint(t *testing.T) { { DashboardID: 2, Version: 1, - Data: fakeDash.Data, + Data: simplejson.NewFromAny(map[string]any{ + "title": "Dash1", + }), }, } From 1adcee17ca25c609b3f53e94c3a33c0031ea28f7 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:56:47 -0600 Subject: [PATCH 39/86] Chore: Add push to `main` trigger to `lint-build-docs` workflow (#102653) * baldm0mma/ add push to main trigger for docs lint and build * baldm0mma/ update naming --- .github/CODEOWNERS | 2 +- .../{pr-lint-build-docs.yml => lint-build-docs.yml} | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) rename .github/workflows/{pr-lint-build-docs.yml => lint-build-docs.yml} (92%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 247bc556cdb..47ab5101416 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -788,6 +788,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/feature-toggles-ci.yml @grafana/docs-tooling /.github/workflows/github-release.yml @grafana/grafana-developer-enablement-squad /.github/workflows/issue-opened.yml @grafana/grafana-community-support +/.github/workflows/lint-build-docs.yml @grafana/docs-tooling /.github/workflows/metrics-collector.yml @torkelo /.github/workflows/milestone.yml @tolzhabayev /.github/workflows/pr-backend-code-checks.yml @grafana/grafana-backend-group @@ -796,7 +797,6 @@ embed.go @grafana/grafana-as-code /.github/workflows/pr-codeql-analysis-javascript.yml @DanCech /.github/workflows/pr-codeql-analysis-python.yml @DanCech /.github/workflows/pr-commands.yml @tolzhabayev -/.github/workflows/pr-lint-build-docs.yml @grafana/docs-tooling /.github/workflows/pr-patch-check.yml @grafana/grafana-developer-enablement-squad /.github/workflows/pr-test-integration.yml @grafana/grafana-backend-group /.github/workflows/pr-backend-coverage.yml @grafana/grafana-backend-group diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/lint-build-docs.yml similarity index 92% rename from .github/workflows/pr-lint-build-docs.yml rename to .github/workflows/lint-build-docs.yml index 8ed36d3da9a..ecd6151e228 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/lint-build-docs.yml @@ -7,6 +7,14 @@ on: - 'docs/**' - 'packages/**/*.md' - 'latest.json' + push: + branches: + - main + paths: + - '*.md' + - 'docs/**' + - 'packages/**/*.md' + - 'latest.json' jobs: docs: From 41cc82b8d032129d2570507c204a0a0f2e8fa955 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Mon, 24 Mar 2025 15:57:00 +0000 Subject: [PATCH 40/86] Combobox: Refactor sortByGroup for performance (#102664) * first pass at improving perf of sortByGroup * polish up --- .../components/Combobox/useOptions.test.ts | 55 ++++++++++++++++- .../src/components/Combobox/useOptions.ts | 60 ++++++++++++------- 2 files changed, 94 insertions(+), 21 deletions(-) diff --git a/packages/grafana-ui/src/components/Combobox/useOptions.test.ts b/packages/grafana-ui/src/components/Combobox/useOptions.test.ts index ff5f5ae61b0..a80d6f01168 100644 --- a/packages/grafana-ui/src/components/Combobox/useOptions.test.ts +++ b/packages/grafana-ui/src/components/Combobox/useOptions.test.ts @@ -1,6 +1,6 @@ import { renderHook, act, waitFor } from '@testing-library/react'; -import { useOptions } from './useOptions'; +import { sortByGroup, useOptions } from './useOptions'; describe('useOptions', () => { it('should handle a large number of synchronous options without throwing an error', () => { @@ -101,3 +101,56 @@ describe('useOptions', () => { expect(result.current.asyncError).toBe(true); }); }); + +describe('sortByGroup', () => { + it('should return original array when no groups exist', () => { + const options = [ + { label: 'Apple', value: 'apple' }, + { label: 'Banana', value: 'banana' }, + { label: 'Carrot', value: 'carrot' }, + ]; + + const { options: sortedOptions, groupStartIndices } = sortByGroup(options); + + expect(sortedOptions).toBe(options); // Check reference equality + expect(groupStartIndices.size).toBe(0); + }); + + it('should return original array when only one group exists', () => { + const options = [ + { label: 'Apple', value: 'apple', group: 'fruits' }, + { label: 'Banana', value: 'banana', group: 'fruits' }, + { label: 'Tomato', value: 'tomato', group: 'fruits' }, + ]; + + const { options: sortedOptions, groupStartIndices } = sortByGroup(options); + + expect(sortedOptions).toEqual(options); + expect(groupStartIndices.size).toBe(1); + expect(groupStartIndices.get('fruits')).toBe(0); + }); + + it('should group options and track group start indices', () => { + const options = [ + { label: 'Apple', value: 'apple', group: 'fruits' }, + { label: 'Carrot', value: 'carrot', group: 'vegetables' }, + { label: 'Banana', value: 'banana', group: 'fruits' }, + { label: 'Celery', value: 'celery', group: 'vegetables' }, + { label: 'Other', value: 'other' }, // Ungrouped + ]; + + const { options: sortedOptions, groupStartIndices } = sortByGroup(options); + + expect(sortedOptions).toEqual([ + { label: 'Apple', value: 'apple', group: 'fruits' }, + { label: 'Banana', value: 'banana', group: 'fruits' }, + { label: 'Carrot', value: 'carrot', group: 'vegetables' }, + { label: 'Celery', value: 'celery', group: 'vegetables' }, + { label: 'Other', value: 'other' }, + ]); + + expect(groupStartIndices.size).toBe(2); + expect(groupStartIndices.get('fruits')).toBe(0); + expect(groupStartIndices.get('vegetables')).toBe(2); + }); +}); diff --git a/packages/grafana-ui/src/components/Combobox/useOptions.ts b/packages/grafana-ui/src/components/Combobox/useOptions.ts index 59ff783786b..bedd6dc1b8b 100644 --- a/packages/grafana-ui/src/components/Combobox/useOptions.ts +++ b/packages/grafana-ui/src/components/Combobox/useOptions.ts @@ -115,41 +115,61 @@ export function useOptions(rawOptions: AsyncOptions(options: Array>) { +/** + * Sorts options by group and returns the sorted options and the starting index of each group + */ +export function sortByGroup(options: Array>) { + // Group options by their group const groupedOptions = new Map>>(); + const groupStartIndices = new Map(); + for (const option of options) { - const groupExists = groupedOptions.has(option.group); - if (groupExists) { - groupedOptions.get(option.group)?.push(option); + const group = option.group; + const existing = groupedOptions.get(group); + if (existing) { + existing.push(option); } else { - groupedOptions.set(option.group, [option]); + groupedOptions.set(group, [option]); } } - // Create a map to track the starting index of each group - const groupStartIndices = new Map(); + // If we only have one group (either the undefined group, or a single group), return the original array + if (groupedOptions.size <= 1) { + if (options[0]?.group) { + groupStartIndices.set(options[0]?.group, 0); + } + + return { + options, + groupStartIndices, + }; + } + + // 'Preallocate' result array with same size as input - very minor optimization + const result: Array> = new Array(options.length); + let currentIndex = 0; - // Reorganize options to have groups first, then undefined group - let reorganizeOptions: Array> = []; + // Fill result array with grouped options for (const [group, groupOptions] of groupedOptions) { - if (!group) { - continue; + if (group) { + groupStartIndices.set(group, currentIndex); + for (const option of groupOptions) { + result[currentIndex++] = option; + } } - - groupStartIndices.set(group, currentIndex); - reorganizeOptions = reorganizeOptions.concat(groupOptions); - currentIndex += groupOptions.length; } - const undefinedGroupOptions = groupedOptions.get(undefined); - if (undefinedGroupOptions) { - groupStartIndices.set('undefined', currentIndex); - reorganizeOptions = reorganizeOptions.concat(undefinedGroupOptions); + // Add ungrouped options at the end + const ungrouped = groupedOptions.get(undefined); + if (ungrouped) { + for (const option of ungrouped) { + result[currentIndex++] = option; + } } return { - options: reorganizeOptions, + options: result, groupStartIndices, }; } From c8881c272f22f54369c8244024e2ffc71e530f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ida=20=C5=A0tambuk?= Date: Mon, 24 Mar 2025 17:59:48 +0100 Subject: [PATCH 41/86] Prometheus: Add back @lezer/highlight to dev dependency (#102632) --- packages/grafana-prometheus/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 7b76365cdc8..43181e20566 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -47,6 +47,7 @@ "@hello-pangea/dnd": "17.0.0", "@leeoniya/ufuzzy": "1.0.18", "@lezer/common": "1.2.3", + "@lezer/highlight": "1.2.1", "@lezer/lr": "1.4.2", "@prometheus-io/lezer-promql": "0.301.0", "@reduxjs/toolkit": "2.5.1", diff --git a/yarn.lock b/yarn.lock index 5e294290c31..b187d3c91e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3351,6 +3351,7 @@ __metadata: "@hello-pangea/dnd": "npm:17.0.0" "@leeoniya/ufuzzy": "npm:1.0.18" "@lezer/common": "npm:1.2.3" + "@lezer/highlight": "npm:1.2.1" "@lezer/lr": "npm:1.4.2" "@prometheus-io/lezer-promql": "npm:0.301.0" "@reduxjs/toolkit": "npm:2.5.1" From 1e4555c79b327c44df9b8061a5e82361a6eecb13 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Mon, 24 Mar 2025 11:59:26 -0600 Subject: [PATCH 42/86] K8s: Folders: fix error conversion (#102737) --- docs/sources/developers/http_api/folder.md | 2 +- pkg/api/apierrors/folder.go | 10 +++++++--- pkg/api/apierrors/folder_test.go | 19 ++++++++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/sources/developers/http_api/folder.md b/docs/sources/developers/http_api/folder.md index 73a9c54ba39..7a65f7fa56d 100644 --- a/docs/sources/developers/http_api/folder.md +++ b/docs/sources/developers/http_api/folder.md @@ -205,7 +205,7 @@ Status Codes: - **400** – Errors (invalid json, missing or invalid fields, etc) - **401** – Unauthorized - **403** – Access Denied -- **409** - Folder already exists +- **412** - Folder already exists ## Update folder diff --git a/pkg/api/apierrors/folder.go b/pkg/api/apierrors/folder.go index b29d5000fad..19cf5323e56 100644 --- a/pkg/api/apierrors/folder.go +++ b/pkg/api/apierrors/folder.go @@ -44,9 +44,13 @@ func ToFolderErrorResponse(err error) response.Response { return response.JSON(http.StatusPreconditionFailed, util.DynMap{"status": "version-mismatch", "message": dashboards.ErrFolderVersionMismatch.Error()}) } - // folder errors are wrapped in an error util, so this is the only way of comparing errors - if err.Error() == folder.ErrMaximumDepthReached.Error() { - return response.JSON(http.StatusBadRequest, util.DynMap{"messageId": "folder.maximum-depth-reached", "message": "Maximum nested folder depth reached"}) + if errors.Is(err, folder.ErrMaximumDepthReached) { + return response.JSON(http.StatusBadRequest, util.DynMap{"messageId": "folder.maximum-depth-reached", "message": folder.ErrMaximumDepthReached.Error()}) + } + + var statusErr *k8sErrors.StatusError + if errors.As(err, &statusErr) { + return response.Error(int(statusErr.ErrStatus.Code), statusErr.ErrStatus.Message, err) } return response.ErrOrFallback(http.StatusInternalServerError, "Folder API error", err) diff --git a/pkg/api/apierrors/folder_test.go b/pkg/api/apierrors/folder_test.go index e3d70bded31..09cd5aba16b 100644 --- a/pkg/api/apierrors/folder_test.go +++ b/pkg/api/apierrors/folder_test.go @@ -10,6 +10,8 @@ import ( "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/util" "github.com/stretchr/testify/require" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestToFolderErrorResponse(t *testing.T) { @@ -66,13 +68,28 @@ func TestToFolderErrorResponse(t *testing.T) { { name: "folder max depth reached", input: folder.ErrMaximumDepthReached, - want: response.JSON(http.StatusBadRequest, util.DynMap{"messageId": "folder.maximum-depth-reached", "message": "Maximum nested folder depth reached"}), + want: response.JSON(http.StatusBadRequest, util.DynMap{"messageId": "folder.maximum-depth-reached", "message": folder.ErrMaximumDepthReached.Error()}), }, { name: "fallback error", input: errors.New("some error"), want: response.ErrOrFallback(http.StatusInternalServerError, "Folder API error", errors.New("some error")), }, + { + name: "kubernetes status error", + input: &k8sErrors.StatusError{ + ErrStatus: metav1.Status{ + Code: 412, + Message: "the folder has been changed by someone else", + }, + }, + want: response.Error(412, "the folder has been changed by someone else", &k8sErrors.StatusError{ + ErrStatus: metav1.Status{ + Code: 412, + Message: "the folder has been changed by someone else", + }, + }), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 8359474a1c72b72f038aa4ac85e2d07371a59ab5 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Mon, 24 Mar 2025 12:35:24 -0600 Subject: [PATCH 43/86] Chore: Add pushes to `main` in the `storybook-verification` workflow (#102496) baldm0mma/ add pushes to main --- .github/workflows/storybook-verification.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/storybook-verification.yml b/.github/workflows/storybook-verification.yml index 72eb6a4ad4c..efab7af6c4c 100644 --- a/.github/workflows/storybook-verification.yml +++ b/.github/workflows/storybook-verification.yml @@ -4,7 +4,13 @@ on: pull_request: paths: - 'packages/grafana-ui/**' - - '.github/workflows/storybook-verification.yml' + - '!docs/**' + - '!*.md' + push: + branches: + - main + paths: + - 'packages/grafana-ui/**' - '!docs/**' - '!*.md' From 53d355813eb74bf056c534416d8674ea2ffeae12 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:09:59 -0600 Subject: [PATCH 44/86] Chore: Migrate `main-lint-backend` drone pipeline to GHAs (#102494) * baldm0mma/ adjust triggers to run on push to main * baldm0mma/ rename and update codeowners --- .github/CODEOWNERS | 2 +- ...kend-code-checks.yml => backend-code-checks.yml} | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) rename .github/workflows/{pr-backend-code-checks.yml => backend-code-checks.yml} (86%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 47ab5101416..55861a74d6f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -768,6 +768,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend /.github/workflows/alerting-update-module.yml @grafana/alerting-backend /.github/workflows/auto-milestone.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backend-code-checks.yml @grafana/grafana-backend-group /.github/workflows/backend-unit-tests.yml @grafana/grafana-backend-group /.github/workflows/backport.yml @grafana/grafana-developer-enablement-squad /.github/workflows/bump-version.yml @grafana/grafana-developer-enablement-squad @@ -791,7 +792,6 @@ embed.go @grafana/grafana-as-code /.github/workflows/lint-build-docs.yml @grafana/docs-tooling /.github/workflows/metrics-collector.yml @torkelo /.github/workflows/milestone.yml @tolzhabayev -/.github/workflows/pr-backend-code-checks.yml @grafana/grafana-backend-group /.github/workflows/pr-checks.yml @tolzhabayev /.github/workflows/pr-codeql-analysis-go.yml @DanCech /.github/workflows/pr-codeql-analysis-javascript.yml @DanCech diff --git a/.github/workflows/pr-backend-code-checks.yml b/.github/workflows/backend-code-checks.yml similarity index 86% rename from .github/workflows/pr-backend-code-checks.yml rename to .github/workflows/backend-code-checks.yml index f9b0b6eeecc..75249775bad 100644 --- a/.github/workflows/pr-backend-code-checks.yml +++ b/.github/workflows/backend-code-checks.yml @@ -7,6 +7,13 @@ on: - '*.md' - 'docs/**' - 'latest.json' + push: + branches: + - main + paths-ignore: + - '*.md' + - 'docs/**' + - 'latest.json' permissions: contents: read @@ -44,8 +51,8 @@ jobs: - name: Generate and Validate OpenAPI Specs run: | - # For forks, we'll just run the basic swagger-gen without validation - if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then + # For PRs from forks, we'll just run the basic swagger-gen without validation + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then echo "PR is from a fork, skipping enterprise-based validation" make swagger-gen exit 0 @@ -54,7 +61,7 @@ jobs: # Clean and regenerate OpenAPI specs make swagger-clean && make openapi3-gen - # Check if the generated specs differ from what's in the PR + # Check if the generated specs differ from what's in the repository for f in public/api-merged.json public/openapi3.json; do git add $f; done if [ -z "$(git diff --name-only --cached)" ]; then echo "OpenAPI specs are up to date!" From 5dd0aa2c732b6052e6ee248f5b5f62c10bb80b76 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Mon, 24 Mar 2025 13:11:04 -0600 Subject: [PATCH 45/86] Revert "Cloudwatch: Migrate to aws-sdk-go-v2" (#102738) Revert "Cloudwatch: Migrate to aws-sdk-go-v2 (#99643)" This reverts commit 5bb1d5f2c37514f19760a888e638bf9daf51e480. --- go.mod | 40 ++- go.sum | 78 +++--- go.work.sum | 6 - pkg/build/go.mod | 2 +- pkg/build/go.sum | 4 +- pkg/storage/unified/apistore/go.mod | 34 +-- pkg/storage/unified/apistore/go.sum | 68 ++--- pkg/storage/unified/resource/go.mod | 34 +-- pkg/storage/unified/resource/go.sum | 68 ++--- pkg/tsdb/cloudwatch/annotation_query.go | 36 ++- pkg/tsdb/cloudwatch/annotation_query_test.go | 17 +- pkg/tsdb/cloudwatch/client_factory.go | 57 +++-- pkg/tsdb/cloudwatch/clients/metrics.go | 55 ++-- pkg/tsdb/cloudwatch/clients/metrics_test.go | 21 +- pkg/tsdb/cloudwatch/cloudwatch.go | 204 ++++++++------- .../cloudwatch/cloudwatch_integration_test.go | 110 ++++---- pkg/tsdb/cloudwatch/cloudwatch_test.go | 80 +++--- .../get_dimension_values_for_wildcards.go | 3 +- ...get_dimension_values_for_wildcards_test.go | 43 ++-- .../cloudwatch/get_metric_data_executor.go | 11 +- .../get_metric_data_executor_test.go | 30 +-- pkg/tsdb/cloudwatch/log_actions.go | 129 ++++++---- pkg/tsdb/cloudwatch/log_actions_test.go | 241 +++++++++++------- pkg/tsdb/cloudwatch/log_query.go | 48 ++-- pkg/tsdb/cloudwatch/log_query_test.go | 96 ++++--- pkg/tsdb/cloudwatch/log_sync_query.go | 8 +- pkg/tsdb/cloudwatch/log_sync_query_test.go | 106 +++++--- .../cloudwatch/metric_data_input_builder.go | 9 +- .../metric_data_input_builder_test.go | 9 +- .../cloudwatch/metric_data_query_builder.go | 27 +- .../metric_data_query_builder_test.go | 5 +- pkg/tsdb/cloudwatch/metric_find_query.go | 84 +++--- pkg/tsdb/cloudwatch/metric_find_query_test.go | 96 +++---- .../cloudwatch/mocks/cloudwatch_metric_api.go | 78 +++--- pkg/tsdb/cloudwatch/mocks/logs.go | 35 +-- pkg/tsdb/cloudwatch/mocks/metrics_client.go | 5 +- pkg/tsdb/cloudwatch/mocks/oam_client.go | 7 +- pkg/tsdb/cloudwatch/mocks/regions.go | 14 +- pkg/tsdb/cloudwatch/models/api.go | 63 ++--- .../cloudwatch/models/cloudwatch_query.go | 29 +-- .../models/cloudwatch_query_test.go | 12 +- pkg/tsdb/cloudwatch/models/logs_query.go | 2 +- .../cloudwatch/models/query_row_response.go | 24 +- .../resources/log_groups_resource_request.go | 8 +- pkg/tsdb/cloudwatch/models/resources/types.go | 6 +- pkg/tsdb/cloudwatch/response_parser.go | 16 +- pkg/tsdb/cloudwatch/response_parser_test.go | 220 ++++++++-------- .../cloudwatch/routes/log_group_fields.go | 2 +- .../routes/log_group_fields_test.go | 4 +- pkg/tsdb/cloudwatch/routes/log_groups.go | 2 +- pkg/tsdb/cloudwatch/routes/log_groups_test.go | 34 +-- pkg/tsdb/cloudwatch/services/accounts.go | 23 +- pkg/tsdb/cloudwatch/services/accounts_test.go | 67 +++-- pkg/tsdb/cloudwatch/services/list_metrics.go | 32 ++- .../cloudwatch/services/list_metrics_test.go | 28 +- pkg/tsdb/cloudwatch/services/log_groups.go | 20 +- .../cloudwatch/services/log_groups_test.go | 149 ++++++----- pkg/tsdb/cloudwatch/services/regions.go | 12 +- pkg/tsdb/cloudwatch/services/regions_test.go | 12 +- pkg/tsdb/cloudwatch/sort_frame_test.go | 2 +- pkg/tsdb/cloudwatch/test_utils.go | 223 ++++++++-------- pkg/tsdb/cloudwatch/time_series_query_test.go | 124 +++++---- 62 files changed, 1617 insertions(+), 1495 deletions(-) diff --git a/go.mod b/go.mod index 9e0d22a8f6e..d0e62735554 100644 --- a/go.mod +++ b/go.mod @@ -29,13 +29,7 @@ require ( github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // @grafana/plugins-platform-backend github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2 v1.36.1 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad @@ -91,7 +85,7 @@ require ( github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend github.com/grafana/grafana-app-sdk v0.31.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.30.0 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana-aws-sdk v0.33.0 // @grafana/aws-datasources + github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources @@ -227,8 +221,6 @@ require ( github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250317130411-3f270d1de043 // @grafana/grafana-search-and-storage ) -require github.com/aws/smithy-go v1.22.2 // @grafana/aws-datasources - require ( cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect @@ -272,23 +264,25 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -378,7 +372,7 @@ require ( github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/grafana-search-and-storage github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/hashicorp/consul/api v1.30.0 // indirect diff --git a/go.sum b/go.sum index 27e7aab477f..96df6362cc9 100644 --- a/go.sum +++ b/go.sum @@ -839,56 +839,46 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 h1:bfHEPSWRqKAUp9ugaYDo6bYmCwYGhpGlcSYbnjpZ4lQ= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9/go.mod h1:w0Sa1DOIjqTBXmwYFk1r+i6Xtkeq21JGjUGe/NCqBHs= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 h1:DddWiL/XVT9GjMZqbYoIpJm5fFa08/CSk7fPN5neWVY= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7/go.mod h1:zZeYjS1D+qvIOiDrCT89Rrm6vSn4m8DNhi0kb3wwzYM= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 h1:3hH6o7Z2WeE1twvz44Aitn6Qz8DZN3Dh5IB4Eh2xq7s= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0/go.mod h1:I76S7jN0nfsYTBtuTgTsJtK2Q8yJVDgrLr5eLN64wMA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= -github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 h1:4IrWw0hu8HEdo+6htGgzrjiiTJeyreyofj1SEZDb5qc= -github.com/aws/aws-sdk-go-v2/service/oam v1.15.13/go.mod h1:nUyUC5TvB8cwPPZjwCkJ6+NsT6TnJCEiLreXDdRQchU= -github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 h1:wdMzCMpoSKRYp4vtciAxPzjJy7wSEQsl0pkvlAJQ+Xo= -github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13/go.mod h1:jhfb2oFQrEqsl6AqYkFlhz1kUys4AWXaFzfA1BCzYWY= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= @@ -1579,8 +1569,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY= @@ -1637,8 +1627,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 h1:SDGrP81Vcd102L3UJEryRd1eestRw73wt+b8vnVEFe0= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 h1:XMreZ1SPjLpd9zhql5FXKFYwAcgBzS2E2MOPx4n+FyY= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2/go.mod h1:UKONJhBCxmL+0ri27VMledCVzZIJqnl6Ah24A5vCRzs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= diff --git a/go.work.sum b/go.work.sum index 91bbc83d04a..04236db57b6 100644 --- a/go.work.sum +++ b/go.work.sum @@ -848,7 +848,6 @@ github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6 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 v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= -github.com/elazarl/goproxy v1.7.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/elazarl/goproxy v1.7.1/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= @@ -993,7 +992,6 @@ github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0ji github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= -github.com/grafana/grafana-plugin-sdk-go v0.266.0/go.mod h1:bxkXrBQ4QSmOncsWdIOcpgP+M6wajQNMAPXlbWrqAWY= github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q= github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= @@ -1011,7 +1009,6 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= @@ -1146,7 +1143,6 @@ github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= github.com/matryer/moq v0.3.3 h1:pScMH9VyrdT4S93yiLpVyU8rCDqGQr24uOyBxmktG5Q= github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M= @@ -1166,7 +1162,6 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -1674,7 +1669,6 @@ golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index ed6f08c804c..14352adba3e 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -10,7 +10,7 @@ replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible require ( cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources github.com/docker/docker v27.5.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 55b8c06217d..1d55ac2a88b 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -53,8 +53,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 7955d73ee7d..a575dbab08b 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -76,26 +76,26 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect - github.com/aws/smithy-go v1.22.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -210,7 +210,7 @@ require ( github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.33.0 // indirect + github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/grafana-plugin-sdk-go v0.272.0 // indirect github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // indirect @@ -219,7 +219,7 @@ require ( github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index bcc6edb5699..4d7afcb8d52 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -735,46 +735,46 @@ github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -1265,8 +1265,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1285,8 +1285,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 3877ed0daa3..689d9b2d97b 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -60,26 +60,26 @@ require ( github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect - github.com/aws/smithy-go v1.22.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bluele/gcache v0.0.2 // indirect @@ -136,11 +136,11 @@ require ( github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.33.0 // indirect + github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 0f72e759646..77e90d5ecf1 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -714,46 +714,46 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ= github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -1160,8 +1160,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1172,8 +1172,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF 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= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog= diff --git a/pkg/tsdb/cloudwatch/annotation_query.go b/pkg/tsdb/cloudwatch/annotation_query.go index be38f571726..2eb61676354 100644 --- a/pkg/tsdb/cloudwatch/annotation_query.go +++ b/pkg/tsdb/cloudwatch/annotation_query.go @@ -7,10 +7,8 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -31,14 +29,14 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC statistic = *model.Statistic } - var period int32 + var period int64 if model.Period != nil && *model.Period != "" { - p, err := strconv.ParseInt(*model.Period, 10, 32) + p, err := strconv.ParseInt(*model.Period, 10, 64) if err != nil { return nil, backend.DownstreamError(fmt.Errorf("query period must be an int")) } - period = int32(p) + period = p } prefixMatching := false @@ -71,11 +69,11 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC if prefixMatching { params := &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), ActionPrefix: actionPrefix, AlarmNamePrefix: alarmNamePrefix, } - resp, err := cli.DescribeAlarms(ctx, params) + resp, err := cli.DescribeAlarms(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarms", err))) return result, nil @@ -86,10 +84,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC return result, backend.DownstreamError(errors.New("invalid annotations query")) } - var qd []cloudwatchtypes.Dimension + var qd []*cloudwatch.Dimension for k, v := range dimensions { for _, vvv := range v.ArrayOfString { - qd = append(qd, cloudwatchtypes.Dimension{ + qd = append(qd, &cloudwatch.Dimension{ Name: aws.String(k), Value: aws.String(vvv), }) @@ -99,10 +97,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC Namespace: aws.String(model.Namespace), MetricName: aws.String(metricName), Dimensions: qd, - Statistic: cloudwatchtypes.Statistic(statistic), - Period: aws.Int32(period), + Statistic: aws.String(statistic), + Period: aws.Int64(period), } - resp, err := cli.DescribeAlarmsForMetric(ctx, params) + resp, err := cli.DescribeAlarmsForMetric(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmsForMetric", err))) return result, nil @@ -118,9 +116,9 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC AlarmName: alarmName, StartDate: aws.Time(query.TimeRange.From), EndDate: aws.Time(query.TimeRange.To), - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), } - resp, err := cli.DescribeAlarmHistory(ctx, params) + resp, err := cli.DescribeAlarmHistory(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmHistory", err))) return result, nil @@ -129,7 +127,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC annotations = append(annotations, &annotationEvent{ Time: *history.Timestamp, Title: *history.AlarmName, - Tags: string(history.HistoryItemType), + Tags: *history.HistoryItemType, Text: *history.HistorySummary, }) } @@ -164,7 +162,7 @@ func transformAnnotationToTable(annotations []*annotationEvent, query backend.Da } func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, metricName string, - dimensions dataquery.Dimensions, statistic string, period int32) []*string { + dimensions dataquery.Dimensions, statistic string, period int64) []*string { alarmNames := make([]*string, 0) for _, alarm := range alarms.MetricAlarms { @@ -191,7 +189,7 @@ func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, met continue } - if string(alarm.Statistic) != statistic { + if *alarm.Statistic != statistic { continue } diff --git a/pkg/tsdb/cloudwatch/annotation_query_test.go b/pkg/tsdb/cloudwatch/annotation_query_test.go index 9dd77fb1f3b..6779c82a896 100644 --- a/pkg/tsdb/cloudwatch/annotation_query_test.go +++ b/pkg/tsdb/cloudwatch/annotation_query_test.go @@ -5,13 +5,12 @@ import ( "encoding/json" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,7 +22,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { }) var client fakeCWAnnotationsClient - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &client } @@ -54,8 +53,8 @@ func TestQuery_AnnotationQuery(t *testing.T) { assert.Equal(t, &cloudwatch.DescribeAlarmsForMetricInput{ Namespace: aws.String("custom"), MetricName: aws.String("CPUUtilization"), - Statistic: "Average", - Period: aws.Int32(300), + Statistic: aws.String("Average"), + Period: aws.Int64(300), }, client.calls.describeAlarmsForMetric[0]) }) @@ -87,7 +86,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { require.Len(t, client.calls.describeAlarms, 1) assert.Equal(t, &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), ActionPrefix: aws.String("some_action_prefix"), AlarmNamePrefix: aws.String("some_alarm_name_prefix"), }, client.calls.describeAlarms[0]) diff --git a/pkg/tsdb/cloudwatch/client_factory.go b/pkg/tsdb/cloudwatch/client_factory.go index 2a683e7772a..258640e7f80 100644 --- a/pkg/tsdb/cloudwatch/client_factory.go +++ b/pkg/tsdb/cloudwatch/client_factory.go @@ -1,53 +1,64 @@ package cloudwatch import ( - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/oam" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/oam" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) -// NewCWClient is a CloudWatch metrics api factory. +// NewMetricsAPI is a CloudWatch metrics api factory. // // Stubbable by tests. -var NewCWClient = func(cfg aws.Config) models.CWClient { - return cloudwatch.NewFromConfig(cfg) +var NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { + return cloudwatch.New(sess) } // NewLogsAPI is a CloudWatch logs api factory. // // Stubbable by tests. -var NewLogsAPI = func(cfg aws.Config) models.CloudWatchLogsAPIProvider { - return cloudwatchlogs.NewFromConfig(cfg) +var NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { + return cloudwatchlogs.New(sess) } -// NewOAMAPI is a CloudWatch OAM API factory +// NewOAMAPI is a CloudWatch OAM api factory. // // Stubbable by tests. -var NewOAMAPI = func(cfg aws.Config) models.OAMAPIProvider { - return oam.NewFromConfig(cfg) +var NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { + return oam.New(sess) } -// NewEC2API is a CloudWatch EC2 API factory +// NewCWClient is a CloudWatch client factory. // -// Stubbable by tests -var NewEC2API = func(cfg aws.Config) models.EC2APIProvider { - return ec2.NewFromConfig(cfg) +// Stubbable by tests. +var NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + return cloudwatch.New(sess) } // NewCWLogsClient is a CloudWatch logs client factory. // // Stubbable by tests. -var NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { - return cloudwatchlogs.NewFromConfig(cfg) +var NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + return cloudwatchlogs.New(sess) +} + +// NewEC2Client is a client factory. +// +// Stubbable by tests. +var NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { + return ec2.New(provider) } -// NewRGTAClient is a ResourceGroupsTaggingAPI Client factory. +// RGTA client factory. // // Stubbable by tests. -var NewRGTAClient = func(cfg aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { - return resourcegroupstaggingapi.NewFromConfig(cfg) +var newRGTAClient = func(provider client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { + return resourcegroupstaggingapi.New(provider) } diff --git a/pkg/tsdb/cloudwatch/clients/metrics.go b/pkg/tsdb/cloudwatch/clients/metrics.go index d3f46606f8e..b3648492a59 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics.go +++ b/pkg/tsdb/cloudwatch/clients/metrics.go @@ -3,42 +3,41 @@ package clients import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -type MetricsClient struct { - cloudwatch.ListMetricsAPIClient - +// this client wraps the CloudWatch API and handles pagination and the composition of the MetricResponse DTO +type metricsClient struct { + models.CloudWatchMetricsAPIProvider listMetricsPageLimit int } -func NewMetricsClient(client cloudwatch.ListMetricsAPIClient, listMetricsPageLimit int) *MetricsClient { - return &MetricsClient{ - ListMetricsAPIClient: client, - listMetricsPageLimit: listMetricsPageLimit, - } +func NewMetricsClient(api models.CloudWatchMetricsAPIProvider, pageLimit int) *metricsClient { + return &metricsClient{CloudWatchMetricsAPIProvider: api, listMetricsPageLimit: pageLimit} } -func (mc *MetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { - var response []resources.MetricResponse - paginator := cloudwatch.NewListMetricsPaginator(mc.ListMetricsAPIClient, params) - includeAccount := params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts - pages := 0 - for paginator.HasMorePages() && pages < mc.listMetricsPageLimit { - pages += 1 - page, err := paginator.NextPage(ctx) - if err != nil { - return response, err - } - for i, metric := range page.Metrics { - resp := resources.MetricResponse{Metric: metric} - if includeAccount && len(page.OwningAccounts) >= i { - resp.AccountId = &page.OwningAccounts[i] +func (l *metricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { + var cloudWatchMetrics []resources.MetricResponse + pageNum := 0 + err := l.ListMetricsPagesWithContext(ctx, params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { + pageNum++ + utils.QueriesTotalCounter.WithLabelValues(utils.ListMetricsLabel).Inc() + metrics, err := awsutil.ValuesAtPath(page, "Metrics") + if err == nil { + for idx, metric := range metrics { + metric := resources.MetricResponse{Metric: metric.(*cloudwatch.Metric)} + if len(page.OwningAccounts) >= idx && params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts { + metric.AccountId = page.OwningAccounts[idx] + } + cloudWatchMetrics = append(cloudWatchMetrics, metric) } - response = append(response, resp) } - } - return response, nil + return !lastPage && pageNum < l.listMetricsPageLimit + }) + + return cloudWatchMetrics, err } diff --git a/pkg/tsdb/cloudwatch/clients/metrics_test.go b/pkg/tsdb/cloudwatch/clients/metrics_test.go index 7f221cc8247..644aecf85e4 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics_test.go +++ b/pkg/tsdb/cloudwatch/clients/metrics_test.go @@ -4,19 +4,16 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMetricsClient(t *testing.T) { - metrics := []cloudwatchtypes.Metric{ + metrics := []*cloudwatch.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, @@ -53,25 +50,25 @@ func TestMetricsClient(t *testing.T) { }) t.Run("Should return account id in case IncludeLinkedAccounts is set to true", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ + fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, - }, OwningAccounts: []string{"1234567890", "1234567890", "1234567895"}} + }, OwningAccounts: []*string{aws.String("1234567890"), aws.String("1234567890"), aws.String("1234567895")}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(true)}) require.NoError(t, err) expected := []resources.MetricResponse{ - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, } assert.Equal(t, expected, response) }) t.Run("Should not return account id in case IncludeLinkedAccounts is set to false", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []string{"1234567890"}} + fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []*string{aws.String("1234567890")}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(false)}) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 88b96836ff9..74ae89e7748 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -3,17 +3,18 @@ package cloudwatch import ( "context" "encoding/json" + "errors" "fmt" - "slices" + "net/http" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - - "github.com/grafana/grafana-aws-sdk/pkg/awsauth" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -36,13 +37,6 @@ const ( // headerFromAlert is used by datasources to identify alert queries headerFromAlert = "FromAlert" - - defaultRegion = "default" - logsQueryMode = "Logs" - // QueryTypes - annotationQuery = "annotationQuery" - logAction = "logAction" - timeSeriesQuery = "timeSeriesQuery" ) type DataQueryJson struct { @@ -51,39 +45,21 @@ type DataQueryJson struct { } type DataSource struct { - Settings models.CloudWatchSettings - ProxyOpts *proxy.Options - AWSConfigProvider awsauth.ConfigProvider - + Settings models.CloudWatchSettings + HTTPClient *http.Client + sessions SessionCache tagValueCache *cache.Cache + ProxyOpts *proxy.Options } -func (ds *DataSource) newAWSConfig(ctx context.Context, region string) (aws.Config, error) { - if region == defaultRegion { - if len(ds.Settings.Region) == 0 { - return aws.Config{}, models.ErrMissingRegion - } - region = ds.Settings.Region - } - authSettings := awsauth.Settings{ - CredentialsProfile: ds.Settings.Profile, - LegacyAuthType: ds.Settings.AuthType, - AssumeRoleARN: ds.Settings.AssumeRoleARN, - ExternalID: ds.Settings.GrafanaSettings.ExternalID, - Endpoint: ds.Settings.Endpoint, - Region: region, - AccessKey: ds.Settings.AccessKey, - SecretKey: ds.Settings.SecretKey, - } - if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { - authSettings.ProxyOptions = ds.ProxyOpts - } - cfg, err := ds.AWSConfigProvider.GetConfig(ctx, authSettings) - if err != nil { - return aws.Config{}, err - } - return cfg, nil -} +const ( + defaultRegion = "default" + logsQueryMode = "Logs" + // QueryTypes + annotationQuery = "annotationQuery" + logAction = "logAction" + timeSeriesQuery = "timeSeriesQuery" +) func ProvideService(httpClientProvider *httpclient.Provider) *CloudWatchService { logger := backend.NewLoggerWith("logger", "tsdb.cloudwatch") @@ -104,7 +80,7 @@ type CloudWatchService struct { } type SessionCache interface { - CredentialsProviderV2(ctx context.Context, cfg awsds.GetSessionConfig) (aws.CredentialsProvider, error) + GetSessionWithAuthSettings(c awsds.GetSessionConfig, as awsds.AuthSettings) (*session.Session, error) } func newExecutor(im instancemgmt.InstanceManager, logger log.Logger) *cloudWatchExecutor { @@ -129,12 +105,18 @@ func NewInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins return nil, err } + httpClient, err := httpClientProvider.New(opts) + if err != nil { + return nil, fmt.Errorf("error creating http client: %w", err) + } + return DataSource{ Settings: instanceSettings, + HTTPClient: httpClient, tagValueCache: cache.New(tagValueCacheExpiration, tagValueCacheExpiration*5), + sessions: awsds.NewSessionCache(), // this is used to build a custom dialer when secure socks proxy is enabled - ProxyOpts: opts.ProxyOptions, - AWSConfigProvider: awsauth.NewConfigProvider(), + ProxyOpts: opts.ProxyOptions, }, nil } } @@ -163,31 +145,30 @@ func instrumentContext(ctx context.Context, endpoint string, pCtx backend.Plugin } func (e *cloudWatchExecutor) getRequestContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.RequestContext, error) { + r := region instance, err := e.getInstance(ctx, pluginCtx) - if err != nil { - return models.RequestContext{}, err - } - if region == defaultRegion { - region = instance.Settings.Region + if err != nil { + return models.RequestContext{}, err + } + r = instance.Settings.Region } - cfg, err := instance.newAWSConfig(ctx, defaultRegion) + ec2Client, err := e.getEC2Client(ctx, pluginCtx, defaultRegion) if err != nil { return models.RequestContext{}, err } - ec2client := NewEC2API(cfg) - cfg, err = instance.newAWSConfig(ctx, region) + sess, err := instance.newSession(r) if err != nil { return models.RequestContext{}, err } return models.RequestContext{ - OAMAPIProvider: NewOAMAPI(cfg), - MetricsClientProvider: clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit), - LogsAPIProvider: NewLogsAPI(cfg), - EC2APIProvider: ec2client, + OAMAPIProvider: NewOAMAPI(sess), + MetricsClientProvider: clients.NewMetricsClient(NewMetricsAPI(sess), instance.Settings.GrafanaSettings.ListMetricsPageLimit), + LogsAPIProvider: NewLogsAPI(sess), + EC2APIProvider: ec2Client, Settings: instance.Settings, Logger: e.logger.FromContext(ctx), }, nil @@ -291,32 +272,86 @@ func (e *cloudWatchExecutor) checkHealthMetrics(ctx context.Context, pluginCtx b return err } - cfg, err := instance.newAWSConfig(ctx, defaultRegion) + session, err := instance.newSession(defaultRegion) if err != nil { return err } - metricClient := clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit) + metricClient := clients.NewMetricsClient(NewMetricsAPI(session), instance.Settings.GrafanaSettings.ListMetricsPageLimit) _, err = metricClient.ListMetricsWithPageLimit(ctx, params) return err } func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error { - cfg, err := e.getAWSConfig(ctx, pluginCtx, defaultRegion) + session, err := e.newSessionFromContext(ctx, pluginCtx, defaultRegion) if err != nil { return err } - logsClient := NewLogsAPI(cfg) - _, err = logsClient.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(1)}) + logsClient := NewLogsAPI(session) + _, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(1)}) return err } -func (e *cloudWatchExecutor) getAWSConfig(ctx context.Context, pluginCtx backend.PluginContext, region string) (aws.Config, error) { +func (ds *DataSource) newSession(region string) (*session.Session, error) { + if region == defaultRegion { + if len(ds.Settings.Region) == 0 { + return nil, models.ErrMissingRegion + } + region = ds.Settings.Region + } + sess, err := ds.sessions.GetSessionWithAuthSettings(awsds.GetSessionConfig{ + // https://github.com/grafana/grafana/issues/46365 + // HTTPClient: instance.HTTPClient, + Settings: awsds.AWSDatasourceSettings{ + Profile: ds.Settings.Profile, + Region: region, + AuthType: ds.Settings.AuthType, + AssumeRoleARN: ds.Settings.AssumeRoleARN, + ExternalID: ds.Settings.ExternalID, + Endpoint: ds.Settings.Endpoint, + DefaultRegion: ds.Settings.Region, + AccessKey: ds.Settings.AccessKey, + SecretKey: ds.Settings.SecretKey, + }, + UserAgentName: aws.String("Cloudwatch")}, + ds.Settings.GrafanaSettings) + if err != nil { + return nil, err + } + + // work around until https://github.com/grafana/grafana/issues/39089 is implemented + if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { + // only update the transport to try to avoid the issue mentioned here https://github.com/grafana/grafana/issues/46365 + // also, 'sess' is cached and reused, so the first time it might have the transport not set, the following uses it will + if sess.Config.HTTPClient.Transport == nil { + // following go standard library logic (https://pkg.go.dev/net/http#Client), if no Transport is provided, + // then we use http.DefaultTransport + defTransport, ok := http.DefaultTransport.(*http.Transport) + if !ok { + // this should not happen but validating just in case + return nil, errors.New("default http client transport is not of type http.Transport") + } + sess.Config.HTTPClient.Transport = defTransport.Clone() + } + err = proxy.New(ds.ProxyOpts).ConfigureSecureSocksHTTPProxy(sess.Config.HTTPClient.Transport.(*http.Transport)) + if err != nil { + return nil, fmt.Errorf("error configuring Secure Socks proxy for Transport: %w", err) + } + } else if sess.Config.HTTPClient != nil { + // Workaround for https://github.com/grafana/grafana/issues/91356 - PDC transport set above + // stays on the cached session after PDC is disabled + sess.Config.HTTPClient.Transport = nil + } + return sess, nil +} + +func (e *cloudWatchExecutor) newSessionFromContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (*session.Session, error) { instance, err := e.getInstance(ctx, pluginCtx) if err != nil { - return aws.Config{}, err + return nil, err } - return instance.newAWSConfig(ctx, region) + + return instance.newSession(region) } func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*DataSource, error) { @@ -329,51 +364,44 @@ func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend. return &instance, nil } -func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWClient, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchiface.CloudWatchAPI, error) { + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewCWClient(cfg), nil + return NewCWClient(sess), nil } -func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWLogsClient, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchlogsiface.CloudWatchLogsAPI, error) { + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - logsClient := NewCWLogsClient(cfg) + logsClient := NewCWLogsClient(sess) return logsClient, nil } func (e *cloudWatchExecutor) getEC2Client(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.EC2APIProvider, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewEC2API(cfg), nil + return NewEC2Client(sess), nil } -func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapi.GetResourcesAPIClient, +func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewRGTAClient(cfg), nil -} - -var terminatedStates = []cloudwatchlogstypes.QueryStatus{ - cloudwatchlogstypes.QueryStatusComplete, - cloudwatchlogstypes.QueryStatusCancelled, - cloudwatchlogstypes.QueryStatusFailed, - cloudwatchlogstypes.QueryStatusTimeout, + return newRGTAClient(sess), nil } -func isTerminated(queryStatus cloudwatchlogstypes.QueryStatus) bool { - return slices.Contains(terminatedStates, queryStatus) +func isTerminated(queryStatus string) bool { + return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout" } diff --git a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go index 7b0cfcf75b9..e9ce1632e21 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go @@ -6,12 +6,12 @@ import ( "net/http" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/ec2" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -27,46 +27,46 @@ import ( func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewCWClient := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2API := NewEC2API - NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } + origNewEC2Client := NewEC2Client + NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } var logApi mocks.LogsAPI - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return &logApi } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegions", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) - NewEC2API = func(aws.Config) models.EC2APIProvider { + ec2Mock.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) + NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { return ec2Mock } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewCWClient = origNewCWClient + NewMetricsAPI = origNewMetricsAPI NewLogsAPI = origNewLogsAPI - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var api mocks.FakeMetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return &api } t.Run("Should handle dimension value request and return values from the api", func(t *testing.T) { - im := testInstanceManager(100, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, + im := testInstanceManager(100) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, }, MetricsPerPage: 100} executor := newExecutor(im, log.NewNullLogger()) @@ -91,18 +91,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle dimension key filter query and return keys from the api", func(t *testing.T) { - im := testInstanceManager(3, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -177,18 +177,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle custom namespace metrics query and return metrics from api", func(t *testing.T) { - im := testInstanceManager(3, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -215,15 +215,15 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { t.Run("Should handle log group fields request", func(t *testing.T) { im := defaultTestInstanceManager() logApi = mocks.LogsAPI{} - logApi.On("GetLogGroupFields", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + logApi.On("GetLogGroupFieldsWithContext", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field1"), - Percent: 50, + Percent: aws.Int64(50), }, { Name: aws.String("field2"), - Percent: 50, + Percent: aws.Int64(50), }, }, }, nil) diff --git a/pkg/tsdb/cloudwatch/cloudwatch_test.go b/pkg/tsdb/cloudwatch/cloudwatch_test.go index e7a2c163bb9..5e36adcda4c 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + awsclient "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -114,21 +114,21 @@ func TestNewInstanceSettings(t *testing.T) { } func Test_CheckHealth(t *testing.T) { - origNewCWClient := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewCWLogsClient := NewCWLogsClient origNewLogsAPI := NewLogsAPI t.Cleanup(func() { - NewCWClient = origNewCWClient + NewMetricsAPI = origNewMetricsAPI NewCWLogsClient = origNewCWLogsClient NewLogsAPI = origNewLogsAPI }) var client fakeCheckHealthClient - NewCWClient = func(aws.Config) models.CWClient { + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return client } - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return client } im := defaultTestInstanceManager() @@ -138,7 +138,8 @@ func Test_CheckHealth(t *testing.T) { executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{ - PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}}) + PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, + }) assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ @@ -149,7 +150,7 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) { client = fakeCheckHealthClient{ - describeLogGroupsFunction: func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + describeLogGroups: func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { return nil, fmt.Errorf("some logs query error") }} @@ -168,8 +169,8 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries logs, fails during metrics query", func(t *testing.T) { client = fakeCheckHealthClient{ - listMetricsFunction: func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return nil, fmt.Errorf("some list metrics error") + listMetricsPages: func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error { + return fmt.Errorf("some list metrics error") }} executor := newExecutor(im, log.NewNullLogger()) @@ -187,7 +188,14 @@ func Test_CheckHealth(t *testing.T) { t.Run("fail to get clients", func(t *testing.T) { client = fakeCheckHealthClient{} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, true) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{ + Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, + sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + return nil, fmt.Errorf("some sessions error") + }}, + }, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -198,14 +206,12 @@ func Test_CheckHealth(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ Status: backend.HealthStatusError, - Message: "1. CloudWatch metrics query failed: LoadDefaultConfig failed\n2. CloudWatch logs query failed: LoadDefaultConfig failed", + Message: "1. CloudWatch metrics query failed: some sessions error\n2. CloudWatch logs query failed: some sessions error", }, resp) }) } -func TestGetAWSConfig_passes_authSettings(t *testing.T) { - // TODO: update this for the new auth structure, or remove it - t.Skip() +func TestNewSession_passes_authSettings(t *testing.T) { ctxDuration := 15 * time.Minute expectedSettings := awsds.AuthSettings{ AllowedAuthProviders: []string{"foo", "bar", "baz"}, @@ -215,7 +221,7 @@ func TestGetAWSConfig_passes_authSettings(t *testing.T) { ListMetricsPageLimit: 50, SecureSocksDSProxyEnabled: true, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + im := datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { return DataSource{ Settings: models.CloudWatchSettings{ AWSDatasourceSettings: awsds.AWSDatasourceSettings{ @@ -223,33 +229,39 @@ func TestGetAWSConfig_passes_authSettings(t *testing.T) { }, GrafanaSettings: expectedSettings, }, + sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + assert.Equal(t, expectedSettings, a) + return &session.Session{ + Config: &aws.Config{}, + }, nil + }}, }, nil - }) + })) executor := newExecutor(im, log.NewNullLogger()) - _, err := executor.getAWSConfig(context.Background(), + _, err := executor.newSessionFromContext(context.Background(), backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, "us-east-1") require.NoError(t, err) } func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewMetricsAPI := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2API := NewEC2API - NewCWClient = func(aws.Config) models.CWClient { return nil } - NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } - NewEC2API = func(aws.Config) models.EC2APIProvider { return nil } + origNewEC2Client := NewEC2Client + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return nil } + NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } + NewEC2Client = func(provider awsclient.ConfigProvider) models.EC2APIProvider { return nil } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewCWClient = origNewMetricsAPI + NewMetricsAPI = origNewMetricsAPI NewLogsAPI = origNewLogsAPI - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var logsApi mocks.LogsAPI - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return &logsApi } @@ -257,8 +269,8 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te t.Run("maps log group api response to resource response of log-groups", func(t *testing.T) { logsApi = mocks.LogsAPI{} - logsApi.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + logsApi.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: aws.String("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: aws.String("group_a")}, }, }, nil) @@ -285,11 +297,11 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te } ]`, string(sender.Response.Body)) - logsApi.AssertCalled(t, "DescribeLogGroups", + logsApi.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"some-account-id"}, + AccountIdentifiers: []*string{utils.Pointer("some-account-id")}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: aws.Int32(50), + Limit: utils.Pointer(int64(50)), LogGroupNamePrefix: utils.Pointer("some-pattern"), }) }) diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go index ddc281c196f..2919304d84a 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/services" - "github.com/patrickmn/go-cache" ) @@ -30,7 +29,7 @@ func shouldSkipFetchingWildcards(ctx context.Context, q *models.CloudWatchQuery) func (e *cloudWatchExecutor) getDimensionValuesForWildcards( ctx context.Context, region string, - client models.CWClient, + client models.CloudWatchMetricsAPIProvider, origQueries []*models.CloudWatchQuery, tagValueCache *cache.Cache, listMetricsPageLimit int, diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go index 2d8058f2b76..5549bc3fc5b 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" @@ -30,10 +29,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) _, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) // make sure the original query wasn't altered @@ -53,8 +52,8 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName2": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{}} - api.On("ListMetrics").Return(nil) + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{}} + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -62,10 +61,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { assert.Equal(t, map[string][]string{"Test_DimensionName2": {}}, queries[0].Dimensions) // Confirm that it calls the api again if the last call did not return any values - api.Metrics = []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, + api.Metrics = []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, } - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err = executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -133,13 +132,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, - {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, + {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -168,13 +167,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { } query.MetricQueryType = models.MetricQueryTypeQuery - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor.go b/pkg/tsdb/cloudwatch/get_metric_data_executor.go index 99395e03452..6ad996ab6e1 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor.go @@ -4,16 +4,15 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/aws" - - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client models.CWClient, +func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwatchiface.CloudWatchAPI, metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) { mdo := make([]*cloudwatch.GetMetricDataOutput, 0) @@ -27,7 +26,7 @@ func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client models.C *metricDataInput.EndTime = metricDataInput.EndTime.Truncate(time.Minute).Add(time.Minute) } - resp, err := client.GetMetricData(ctx, metricDataInput) + resp, err := client.GetMetricDataWithContext(ctx, metricDataInput) if err != nil { return mdo, backend.DownstreamError(err) } diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go index b940fb0f4de..3485daff5da 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go @@ -5,10 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/stretchr/testify/assert" @@ -20,37 +18,37 @@ func TestGetMetricDataExecutorTestRequest(t *testing.T) { t.Run("Should round up end time if cloudWatchRoundUpEndTime is enabled", func(t *testing.T) { executor := &cloudWatchExecutor{} queryEndTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:45:04Z") - inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{}}}, }, nil).Once() _, err := executor.executeRequest(contextWithFeaturesEnabled(features.FlagCloudWatchRoundUpEndTime), mockMetricClient, inputs) require.NoError(t, err) expectedTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:46:00Z") - expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} - mockMetricClient.AssertCalled(t, "GetMetricData", mock.Anything, expectedInput, mock.Anything) + expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} + mockMetricClient.AssertCalled(t, "GetMetricDataWithContext", mock.Anything, expectedInput, mock.Anything) }) } func TestGetMetricDataExecutorTestResponse(t *testing.T) { executor := &cloudWatchExecutor{} - inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []*cloudwatch.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{12.3, 23.5}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(12.3), aws.Float64(23.5)}}}, NextToken: aws.String("next"), }, nil).Once() - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{100}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(100)}}}, }, nil).Once() res, err := executor.executeRequest(context.Background(), mockMetricClient, inputs) require.NoError(t, err) require.Len(t, res, 2) require.Len(t, res[0].MetricDataResults[0].Values, 2) - assert.Equal(t, 23.5, res[0].MetricDataResults[0].Values[1]) - assert.Equal(t, 100.0, res[1].MetricDataResults[0].Values[0]) + assert.Equal(t, 23.5, *res[0].MetricDataResults[0].Values[1]) + assert.Equal(t, 100.0, *res[1].MetricDataResults[0].Values[0]) } diff --git a/pkg/tsdb/cloudwatch/log_actions.go b/pkg/tsdb/cloudwatch/log_actions.go index fdc197183af..80572895974 100644 --- a/pkg/tsdb/cloudwatch/log_actions.go +++ b/pkg/tsdb/cloudwatch/log_actions.go @@ -10,12 +10,11 @@ import ( "strings" "time" - "github.com/aws/smithy-go" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "golang.org/x/sync/errgroup" @@ -26,8 +25,10 @@ import ( ) const ( - defaultEventLimit = int32(10) - defaultLogGroupLimit = int32(50) + limitExceededException = "LimitExceededException" + throttlingException = "ThrottlingException" + defaultEventLimit = int64(10) + defaultLogGroupLimit = int64(50) logIdentifierInternal = "__log__grafana_internal__" logStreamIdentifierInternal = "__logstream__grafana_internal__" ) @@ -42,6 +43,45 @@ func (e *AWSError) Error() string { return fmt.Sprintf("CloudWatch error: %s: %s", e.Code, e.Message) } +// StartQueryInputWithLanguage copies the StartQueryInput struct from aws-sdk-go@v1.55.5 +// (https://github.com/aws/aws-sdk-go/blob/7112c0a0c2d01713a9db2d57f0e5722225baf5b5/service/cloudwatchlogs/api.go#L19541) +// to add support for the new QueryLanguage parameter, which is unlikely to be backported +// since v1 of the aws-sdk-go is in maintenance mode. We've removed the comments for +// clarity. +type StartQueryInputWithLanguage struct { + _ struct{} `type:"structure"` + + EndTime *int64 `locationName:"endTime" type:"long" required:"true"` + Limit *int64 `locationName:"limit" min:"1" type:"integer"` + LogGroupIdentifiers []*string `locationName:"logGroupIdentifiers" type:"list"` + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` + LogGroupNames []*string `locationName:"logGroupNames" type:"list"` + QueryString *string `locationName:"queryString" type:"string" required:"true"` + // QueryLanguage is the only change here from the original code. + QueryLanguage *string `locationName:"queryLanguage" type:"string"` + StartTime *int64 `locationName:"startTime" type:"long" required:"true"` +} +type WithQueryLanguageFunc func(language *dataquery.LogsQueryLanguage) func(*request.Request) + +// WithQueryLanguage assigns the function to a variable in order to mock it in log_actions_test.go +var WithQueryLanguage WithQueryLanguageFunc = withQueryLanguage + +func withQueryLanguage(language *dataquery.LogsQueryLanguage) func(request *request.Request) { + return func(request *request.Request) { + sqi := request.Params.(*cloudwatchlogs.StartQueryInput) + request.Params = &StartQueryInputWithLanguage{ + EndTime: sqi.EndTime, + Limit: sqi.Limit, + LogGroupIdentifiers: sqi.LogGroupIdentifiers, + LogGroupName: sqi.LogGroupName, + LogGroupNames: sqi.LogGroupNames, + QueryString: sqi.QueryString, + QueryLanguage: (*string)(language), + StartTime: sqi.StartTime, + } + } +} + func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { resp := backend.NewQueryDataResponse() @@ -128,35 +168,37 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logsQuery mod return data, nil } -func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*data.Frame, error) { limit := defaultEventLimit if logsQuery.Limit != nil && *logsQuery.Limit > 0 { limit = *logsQuery.Limit } + + queryRequest := &cloudwatchlogs.GetLogEventsInput{ + Limit: aws.Int64(limit), + StartFromHead: aws.Bool(logsQuery.StartFromHead), + } + if logsQuery.LogGroupName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logGroupName' is required")) } + queryRequest.SetLogGroupName(logsQuery.LogGroupName) + if logsQuery.LogStreamName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logStreamName' is required")) } - - queryRequest := &cloudwatchlogs.GetLogEventsInput{ - Limit: aws.Int32(limit), - StartFromHead: aws.Bool(logsQuery.StartFromHead), - LogGroupName: &logsQuery.LogGroupName, - LogStreamName: &logsQuery.LogStreamName, - } + queryRequest.SetLogStreamName(logsQuery.LogStreamName) if logsQuery.StartTime != nil && *logsQuery.StartTime != 0 { - queryRequest.StartTime = logsQuery.StartTime + queryRequest.SetStartTime(*logsQuery.StartTime) } if logsQuery.EndTime != nil && *logsQuery.EndTime != 0 { - queryRequest.EndTime = logsQuery.EndTime + queryRequest.SetEndTime(*logsQuery.EndTime) } - logEvents, err := logsClient.GetLogEvents(ctx, queryRequest) + logEvents, err := logsClient.GetLogEventsWithContext(ctx, queryRequest) if err != nil { return nil, backend.DownstreamError(err) } @@ -181,7 +223,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient return data.NewFrame("logEvents", timestampField, messageField), nil } -func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, timeRange backend.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) { startTime := timeRange.From endTime := timeRange.To @@ -225,34 +267,34 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient m // due to a bug in the startQuery api, we remove * from the arn, otherwise it throws an error logGroupIdentifiers = append(logGroupIdentifiers, strings.TrimSuffix(arn, "*")) } - startQueryInput.LogGroupIdentifiers = logGroupIdentifiers + startQueryInput.LogGroupIdentifiers = aws.StringSlice(logGroupIdentifiers) } else { // even though log group names are being phased out, we still need to support them for backwards compatibility and alert queries - startQueryInput.LogGroupNames = logsQuery.LogGroupNames + startQueryInput.LogGroupNames = aws.StringSlice(logsQuery.LogGroupNames) } } if logsQuery.Limit != nil { - startQueryInput.Limit = aws.Int32(*logsQuery.Limit) - } - if logsQuery.QueryLanguage != nil { - startQueryInput.QueryLanguage = cloudwatchlogstypes.QueryLanguage(*logsQuery.QueryLanguage) + startQueryInput.Limit = aws.Int64(*logsQuery.Limit) } e.logger.FromContext(ctx).Debug("Calling startquery with context with input", "input", startQueryInput) - resp, err := logsClient.StartQuery(ctx, startQueryInput) + resp, err := logsClient.StartQueryWithContext(ctx, startQueryInput, WithQueryLanguage(logsQuery.QueryLanguage)) if err != nil { - if errors.Is(err, &cloudwatchlogstypes.LimitExceededException{}) { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", err) - } else if errors.Is(err, &cloudwatchlogstypes.ThrottlingException{}) { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", err) + var awsErr awserr.Error + if errors.As(err, &awsErr) && awsErr.Code() == "LimitExceededException" { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", awsErr) + err = &AWSError{Code: limitExceededException, Message: err.Error()} + } else if errors.As(err, &awsErr) && awsErr.Code() == "ThrottlingException" { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", awsErr) + err = &AWSError{Code: throttlingException, Message: err.Error()} } err = backend.DownstreamError(err) } return resp, err } -func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, timeRange backend.TimeRange, refID string) (*data.Frame, error) { startQueryResponse, err := e.executeStartQuery(ctx, logsClient, logsQuery, timeRange) if err != nil { @@ -276,19 +318,20 @@ func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient mo return dataFrame, nil } -func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*cloudwatchlogs.StopQueryOutput, error) { queryInput := &cloudwatchlogs.StopQueryInput{ QueryId: aws.String(logsQuery.QueryId), } - response, err := logsClient.StopQuery(ctx, queryInput) + response, err := logsClient.StopQueryWithContext(ctx, queryInput) if err != nil { // If the query has already stopped by the time CloudWatch receives the stop query request, // an "InvalidParameterException" error is returned. For our purposes though the query has been // stopped, so we ignore the error. - if errors.Is(err, &cloudwatchlogstypes.InvalidParameterException{}) { - response = &cloudwatchlogs.StopQueryOutput{Success: false} + var awsErr awserr.Error + if errors.As(err, &awsErr) && awsErr.Code() == "InvalidParameterException" { + response = &cloudwatchlogs.StopQueryOutput{Success: aws.Bool(false)} err = nil } else { err = backend.DownstreamError(err) @@ -298,35 +341,35 @@ func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient mo return response, err } -func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*data.Frame, error) { response, err := e.executeStopQuery(ctx, logsClient, logsQuery) if err != nil { return nil, err } - dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{response.Success})) + dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{*response.Success})) return dataFrame, nil } -func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) { queryInput := &cloudwatchlogs.GetQueryResultsInput{ QueryId: aws.String(logsQuery.QueryId), } - getQueryResultsResponse, err := logsClient.GetQueryResults(ctx, queryInput) + getQueryResultsResponse, err := logsClient.GetQueryResultsWithContext(ctx, queryInput) if err != nil { - var awsErr smithy.APIError + var awsErr awserr.Error if errors.As(err, &awsErr) { - err = &AWSError{Code: awsErr.ErrorCode(), Message: awsErr.ErrorMessage()} + err = &AWSError{Code: awsErr.Code(), Message: err.Error()} } err = backend.DownstreamError(err) } return getQueryResultsResponse, err } -func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, refID string) (*data.Frame, error) { getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, logsQuery) if err != nil { diff --git a/pkg/tsdb/cloudwatch/log_actions_test.go b/pkg/tsdb/cloudwatch/log_actions_test.go index 0cddf9f10b1..ae06b447820 100644 --- a/pkg/tsdb/cloudwatch/log_actions_test.go +++ b/pkg/tsdb/cloudwatch/log_actions_test.go @@ -6,15 +6,19 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -32,7 +36,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } const refID = "A" @@ -53,7 +57,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { EndTime: aws.Int64(1), - Limit: aws.Int32(10), + Limit: aws.Int64(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(false), @@ -72,7 +76,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { StartTime: aws.Int64(1), - Limit: aws.Int32(10), + Limit: aws.Int64(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(true), @@ -84,7 +88,11 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) + executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -100,8 +108,8 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents }) require.NoError(t, err) - require.Len(t, cli.calls.getEvents, 1) - assert.Equal(t, test.expectedInput, cli.calls.getEvents) + require.Len(t, cli.calls.getEventsWithContext, 1) + assert.Equal(t, test.expectedInput, cli.calls.getEventsWithContext) }) } } @@ -112,15 +120,17 @@ func TestQuery_GetLogEvents_returns_response_from_GetLogEvents_to_data_frame_fie NewCWLogsClient = origNewCWLogsClient }) var cli *mocks.MockLogEvents - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return cli } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) cli = &mocks.MockLogEvents{} - cli.On("GetLogEvents", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ - Events: []cloudwatchlogstypes.OutputLogEvent{{ + cli.On("GetLogEventsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ + Events: []*cloudwatchlogs.OutputLogEvent{{ Message: utils.Pointer("some message"), Timestamp: utils.Pointer(int64(15)), }}}, nil) @@ -164,7 +174,7 @@ func TestQuery_StartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } @@ -173,18 +183,18 @@ func TestQuery_StartQuery(t *testing.T) { cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, @@ -195,11 +205,13 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584700643, 0), } - im := testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, false) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -229,18 +241,18 @@ func TestQuery_StartQuery(t *testing.T) { const refID = "A" cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, @@ -251,11 +263,13 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584873443000, 0), } - im := testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, false) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -297,6 +311,26 @@ func TestQuery_StartQuery(t *testing.T) { }) } +type withQueryLanguageMock struct { + capturedLanguage *dataquery.LogsQueryLanguage + mockWithQueryLanguage func(language *dataquery.LogsQueryLanguage) func(request *request.Request) +} + +func newWithQueryLanguageMock() *withQueryLanguageMock { + mock := &withQueryLanguageMock{ + capturedLanguage: new(dataquery.LogsQueryLanguage), + } + + mock.mockWithQueryLanguage = func(language *dataquery.LogsQueryLanguage) func(request *request.Request) { + *mock.capturedLanguage = *language + return func(req *request.Request) { + + } + } + + return mock +} + func Test_executeStartQuery(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { @@ -305,15 +339,15 @@ func Test_executeStartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } - t.Run("successfully parses information from JSON to StartQuery for language", func(t *testing.T) { + t.Run("successfully parses information from JSON to StartQueryWithContext for language", func(t *testing.T) { testCases := map[string]struct { queries []backend.DataQuery expectedOutput []*cloudwatchlogs.StartQueryInput - queryLanguage cloudwatchlogstypes.QueryLanguage + queryLanguage dataquery.LogsQueryLanguage }{ "not defined": { queries: []backend.DataQuery{ @@ -332,12 +366,11 @@ func Test_executeStartQuery(t *testing.T) { expectedOutput: []*cloudwatchlogs.StartQueryInput{{ StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }}, - queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + queryLanguage: dataquery.LogsQueryLanguageCWLI, }, "CWLI": { queries: []backend.DataQuery{{ @@ -356,13 +389,12 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + queryLanguage: dataquery.LogsQueryLanguageCWLI, }, "PPL": { queries: []backend.DataQuery{{ @@ -381,13 +413,12 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("source logs | fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguagePpl, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguagePpl, + queryLanguage: dataquery.LogsQueryLanguagePPL, }, "SQL": { queries: []backend.DataQuery{ @@ -408,35 +439,46 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("SELECT * FROM logs"), LogGroupNames: nil, - QueryLanguage: cloudwatchlogstypes.QueryLanguageSql, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguageSql, + queryLanguage: dataquery.LogsQueryLanguageSQL, }, } for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) + languageMock := newWithQueryLanguageMock() + originalWithQueryLanguage := WithQueryLanguage + WithQueryLanguage = languageMock.mockWithQueryLanguage + defer func() { + WithQueryLanguage = originalWithQueryLanguage + }() + _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, Queries: test.queries, }) assert.NoError(t, err) - assert.Equal(t, test.expectedOutput, cli.calls.startQuery) + assert.Equal(t, test.expectedOutput, cli.calls.startQueryWithContext) + assert.Equal(t, &test.queryLanguage, languageMock.capturedLanguage) }) } }) t.Run("does not populate StartQueryInput.limit when no limit provided", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -454,13 +496,15 @@ func Test_executeStartQuery(t *testing.T) { }) assert.NoError(t, err) - require.Len(t, cli.calls.startQuery, 1) - assert.Nil(t, cli.calls.startQuery[0].Limit) + require.Len(t, cli.calls.startQueryWithContext, 1) + assert.Nil(t, cli.calls.startQueryWithContext[0].Limit) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -486,17 +530,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"fakeARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("fakeARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled and strips out trailing *", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -521,17 +566,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"*fake**ARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("uses LogGroupNames if the cross account feature flag is not enabled, and log group names is present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -555,17 +601,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"/log-group-name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("/log-group-name")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("ignores logGroups if feature flag is disabled even if logGroupNames is not present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -588,17 +635,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: nil, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("it always uses logGroups when feature flag is enabled and ignores log group names", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -622,12 +670,11 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"*fake**ARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) } @@ -639,30 +686,32 @@ func TestQuery_StopQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) timeRange := backend.TimeRange{ From: time.Unix(1584873443, 0), @@ -709,14 +758,14 @@ func TestQuery_GetQueryResults(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } const refID = "A" cli = fakeCWLogsClient{ queryResults: cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { { Field: aws.String("@timestamp"), @@ -746,16 +795,18 @@ func TestQuery_GetQueryResults(t *testing.T) { }, }, }, - Statistics: &cloudwatchlogstypes.QueryStatistics{ - BytesScanned: 512, - RecordsMatched: 256, - RecordsScanned: 1024, + Statistics: &cloudwatchlogs.QueryStatistics{ + BytesScanned: aws.Float64(512), + RecordsMatched: aws.Float64(256), + RecordsScanned: aws.Float64(1024), }, - Status: "Complete", + Status: aws.String("Complete"), }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/log_query.go b/pkg/tsdb/cloudwatch/log_query.go index e4f8b0e4dc3..eab124843f6 100644 --- a/pkg/tsdb/cloudwatch/log_query.go +++ b/pkg/tsdb/cloudwatch/log_query.go @@ -7,9 +7,7 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana-plugin-sdk-go/data" ) @@ -20,7 +18,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return nil, fmt.Errorf("response is nil, cannot convert log results to data frames") } - nonEmptyRows := make([][]cloudwatchlogstypes.ResultField, 0) + nonEmptyRows := make([][]*cloudwatchlogs.ResultField, 0) for _, row := range response.Results { // Sometimes CloudWatch can send empty rows if len(row) == 0 { @@ -117,20 +115,26 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro queryStats := make([]data.QueryStat, 0) if response.Statistics != nil { - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, - Value: response.Statistics.BytesScanned, - }) - - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, - Value: response.Statistics.RecordsScanned, - }) - - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, - Value: response.Statistics.RecordsMatched, - }) + if response.Statistics.BytesScanned != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, + Value: *response.Statistics.BytesScanned, + }) + } + + if response.Statistics.RecordsScanned != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, + Value: *response.Statistics.RecordsScanned, + }) + } + + if response.Statistics.RecordsMatched != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, + Value: *response.Statistics.RecordsMatched, + }) + } } frame := data.NewFrame("CloudWatchLogsResponse", newFields...) @@ -143,8 +147,10 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro frame.Meta.Stats = queryStats } - frame.Meta.Custom = map[string]any{ - "Status": string(response.Status), + if response.Status != nil { + frame.Meta.Custom = map[string]any{ + "Status": *response.Status, + } } // Results aren't guaranteed to come ordered by time (ascending), so we need to sort @@ -152,7 +158,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return frame, nil } -func changeToStringField(lengthOfValues int, rows [][]cloudwatchlogstypes.ResultField, logEventField string) []*string { +func changeToStringField(lengthOfValues int, rows [][]*cloudwatchlogs.ResultField, logEventField string) []*string { fieldValuesAsStrings := make([]*string, lengthOfValues) for i, resultFields := range rows { for _, field := range resultFields { diff --git a/pkg/tsdb/cloudwatch/log_query_test.go b/pkg/tsdb/cloudwatch/log_query_test.go index 9e86b4e752c..a112451915e 100644 --- a/pkg/tsdb/cloudwatch/log_query_test.go +++ b/pkg/tsdb/cloudwatch/log_query_test.go @@ -5,10 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" @@ -21,63 +19,63 @@ import ( func TestLogsResultsToDataframes(t *testing.T) { fakeCloudwatchResponse := &cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 15:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 1"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 16:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 2"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, @@ -86,47 +84,47 @@ func TestLogsResultsToDataframes(t *testing.T) { {}, // or rows with only timestamp { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 3"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, }, - Status: "ok", - Statistics: &cloudwatchlogstypes.QueryStatistics{ - BytesScanned: 2000, - RecordsMatched: 3, - RecordsScanned: 5000, + Status: aws.String("ok"), + Statistics: &cloudwatchlogs.QueryStatistics{ + BytesScanned: aws.Float64(2000), + RecordsMatched: aws.Float64(3), + RecordsScanned: aws.Float64(5000), }, } @@ -226,33 +224,33 @@ func TestLogsResultsToDataframes(t *testing.T) { func TestLogsResultsToDataframes_MixedTypes_NumericValuesMixedWithStringFallBackToStringValues(t *testing.T) { dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("-1.234"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("1"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("not a number"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("2.000"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{}) require.NoError(t, err) @@ -286,27 +284,27 @@ func TestLogsResultsToDataframes_With_Millisecond_Timestamps(t *testing.T) { ingestionTimeField := int64(1732790372916) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ingestionTime"), Value: aws.String(fmt.Sprintf("%d", ingestionTimeField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("stringTimeField"), Value: aws.String(stringTimeField), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("message"), Value: aws.String("log message"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{}) require.NoError(t, err) @@ -350,23 +348,23 @@ func TestLogsResultsToDataframes_With_Int_Grouping_Field(t *testing.T) { timestampField := int64(1732749534876) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberField"), Value: aws.String("8"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("groupingNumber"), Value: aws.String("100"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{"groupingNumber"}) require.NoError(t, err) diff --git a/pkg/tsdb/cloudwatch/log_sync_query.go b/pkg/tsdb/cloudwatch/log_sync_query.go index a2ee420bf4e..fb3f099a9b4 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query.go +++ b/pkg/tsdb/cloudwatch/log_sync_query.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -86,7 +86,7 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req * return resp, nil } -func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, queryContext backend.DataQuery, logsQuery models.LogsQuery, logsTimeout time.Duration) (*cloudwatchlogs.GetQueryResultsOutput, error) { startQueryOutput, err := e.executeStartQuery(ctx, logsClient, logsQuery, queryContext.TimeRange) if err != nil { @@ -117,7 +117,7 @@ func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient models.CW if err != nil { return nil, err } - if isTerminated(res.Status) { + if isTerminated(*res.Status) { return res, err } if time.Duration(attemptCount)*time.Second >= logsTimeout { diff --git a/pkg/tsdb/cloudwatch/log_sync_query_test.go b/pkg/tsdb/cloudwatch/log_sync_query_test.go index eeb228dac8b..cb1f8d0cab3 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query_test.go +++ b/pkg/tsdb/cloudwatch/log_sync_query_test.go @@ -7,12 +7,14 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -29,13 +31,16 @@ func Test_executeSyncLogQuery(t *testing.T) { }) var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } t.Run("getCWLogsClient is called with region from input JSON", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := defaultTestInstanceManager() + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + sess := fakeSessionCache{} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &sess}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -53,12 +58,15 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - //assert.Equal(t, []string{"some region"}, sess.calledRegions) + assert.Equal(t, []string{"some region"}, sess.calledRegions) }) t.Run("getCWLogsClient is called with region from instance manager when region is default", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + sess := fakeSessionCache{} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &sess}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -76,7 +84,7 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - //assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) + assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) }) t.Run("with header", func(t *testing.T) { @@ -111,8 +119,10 @@ func Test_executeSyncLogQuery(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { syncCalled = false - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -151,8 +161,10 @@ func Test_executeSyncLogQuery(t *testing.T) { executeSyncLogQuery = origExecuteSyncLogQuery }) - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -180,17 +192,19 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }) var cli *mockLogsSyncClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return cli } t.Run("when a query refId is not provided, 'A' is assigned by default", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) - im := defaultTestInstanceManager() + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -213,11 +227,13 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { t.Run("when a query refId is provided, it is returned in the response", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) - im := defaultTestInstanceManager() + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -253,38 +269,40 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { // when each query has a different response from AWS API calls, the RefIds are correctly reassigned to the associated response. cli = &mockLogsSyncClient{} // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for A" - cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for A" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for A"), }, nil) // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for B" - cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for B" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for B"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for A" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for A" - Results: [][]cloudwatchlogstypes.ResultField{{{ + Results: [][]*cloudwatchlogs.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("A result"), }}}, - Status: "Complete"}, nil) - cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + Status: aws.String("Complete")}, nil) + cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for B" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for B" - Results: [][]cloudwatchlogstypes.ResultField{{{ + Results: [][]*cloudwatchlogs.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("B result"), }}}, - Status: "Complete"}, nil) + Status: aws.String("Complete")}, nil) - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -310,24 +328,26 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) - expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResults matches the input RefId A + expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId A assert.NoError(t, err) respA, ok := res.Responses["A"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromFirstCall}, respA.Frames[0].Fields) - expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResults matches the input RefId B + expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId B respB, ok := res.Responses["B"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromSecondCall}, respB.Frames[0].Fields) }) t.Run("when logsTimeout setting is defined, the polling period will be set to that variable", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Running"}, nil) - im := testInstanceManagerWithSettings(models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, false) + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Running")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -346,19 +366,21 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) assert.Error(t, err) - cli.AssertNumberOfCalls(t, "GetQueryResults", 1) + cli.AssertNumberOfCalls(t, "GetQueryResultsWithContext", 1) }) t.Run("when getQueryResults returns aws error is returned, it keeps the context", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return( - &cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, - &fakeSmithyError{code: "foo", message: "bar"}, + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return( + &cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, + &fakeAWSError{code: "foo", message: "bar"}, ) - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder.go b/pkg/tsdb/cloudwatch/metric_data_input_builder.go index b659127c658..2bbf4da5df4 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder.go @@ -4,9 +4,8 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) @@ -16,13 +15,13 @@ func (e *cloudWatchExecutor) buildMetricDataInput(ctx context.Context, startTime metricDataInput := &cloudwatch.GetMetricDataInput{ StartTime: aws.Time(startTime), EndTime: aws.Time(endTime), - ScanBy: cloudwatchtypes.ScanByTimestampAscending, + ScanBy: aws.String("TimestampAscending"), } shouldSetLabelOptions := len(queries) > 0 && len(queries[0].TimezoneUTCOffset) > 0 if shouldSetLabelOptions { - metricDataInput.LabelOptions = &cloudwatchtypes.LabelOptions{ + metricDataInput.LabelOptions = &cloudwatch.LabelOptions{ Timezone: aws.String(queries[0].TimezoneUTCOffset), } } diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go index efb7e1d5a82..5b8ba2beb53 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go @@ -5,9 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,9 +20,9 @@ func TestMetricDataInputBuilder(t *testing.T) { tests := []struct { name string timezoneUTCOffset string - expectedLabelOptions *cloudwatchtypes.LabelOptions + expectedLabelOptions *cloudwatch.LabelOptions }{ - {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatchtypes.LabelOptions{Timezone: aws.String("+1234")}}, + {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatch.LabelOptions{Timezone: aws.String("+1234")}}, {name: "when timezoneUTCOffset is not provided", timezoneUTCOffset: "", expectedLabelOptions: nil}, } diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder.go b/pkg/tsdb/cloudwatch/metric_data_query_builder.go index 2edb4f35be8..fbed54f5950 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder.go @@ -4,10 +4,11 @@ import ( "context" "fmt" "sort" + "strconv" "strings" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -15,8 +16,8 @@ import ( const keySeparator = "|&|" -func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (cloudwatchtypes.MetricDataQuery, error) { - mdq := cloudwatchtypes.MetricDataQuery{ +func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) { + mdq := &cloudwatch.MetricDataQuery{ Id: aws.String(query.Id), ReturnData: aws.Bool(query.ReturnData), } @@ -27,10 +28,10 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo switch query.GetGetMetricDataAPIMode() { case models.GMDApiModeMathExpression: - mdq.Period = aws.Int32(int32(query.Period)) + mdq.Period = aws.Int64(int64(query.Period)) mdq.Expression = aws.String(query.Expression) case models.GMDApiModeSQLExpression: - mdq.Period = aws.Int32(int32(query.Period)) + mdq.Period = aws.Int64(int64(query.Period)) mdq.Expression = aws.String(query.SqlExpression) case models.GMDApiModeInferredSearchExpression: mdq.Expression = aws.String(buildSearchExpression(query, query.Statistic)) @@ -38,17 +39,17 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo mdq.Label = aws.String(buildSearchExpressionLabel(query)) } case models.GMDApiModeMetricStat: - mdq.MetricStat = &cloudwatchtypes.MetricStat{ - Metric: &cloudwatchtypes.Metric{ + mdq.MetricStat = &cloudwatch.MetricStat{ + Metric: &cloudwatch.Metric{ Namespace: aws.String(query.Namespace), MetricName: aws.String(query.MetricName), - Dimensions: make([]cloudwatchtypes.Dimension, 0), + Dimensions: make([]*cloudwatch.Dimension, 0), }, - Period: aws.Int32(int32(query.Period)), + Period: aws.Int64(int64(query.Period)), } for key, values := range query.Dimensions { mdq.MetricStat.Metric.Dimensions = append(mdq.MetricStat.Metric.Dimensions, - cloudwatchtypes.Dimension{ + &cloudwatch.Dimension{ Name: aws.String(key), Value: aws.String(values[0]), }) @@ -120,14 +121,14 @@ func buildSearchExpression(query *models.CloudWatchQuery, stat string) string { } schema = fmt.Sprintf("{%s}", schema) schemaSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{schema, searchTerm, account}, " ")) - return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %d))", schemaSearchTermAndAccount, stat, query.Period) + return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %s))", schemaSearchTermAndAccount, stat, strconv.Itoa(query.Period)) } sort.Strings(dimensionNamesWithoutKnownValues) searchTerm = appendSearch(searchTerm, join(dimensionNamesWithoutKnownValues, " ", `"`, `"`)) namespace := fmt.Sprintf("Namespace=%q", query.Namespace) namespaceSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{namespace, searchTerm, account}, " ")) - return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %d))`, namespaceSearchTermAndAccount, stat, query.Period) + return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %s))`, namespaceSearchTermAndAccount, stat, strconv.Itoa(query.Period)) } func buildSearchExpressionLabel(query *models.CloudWatchQuery) string { diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go index e6e74b8acf5..907516f0d80 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - + "github.com/aws/aws-sdk-go/aws" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -89,7 +88,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) - assert.Equal(t, int32(300), *mdq.Period) + assert.Equal(t, int64(300), *mdq.Period) assert.Equal(t, `SUM([a,b])`, *mdq.Expression) }) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index ce618787859..77b41f1669a 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -12,14 +12,10 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/grafana/grafana-plugin-sdk-go/backend" ) @@ -47,7 +43,7 @@ func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCt region := parameters.Get("region") instanceId := parameters.Get("instanceId") - instanceIds := parseMultiSelectValue(instanceId) + instanceIds := aws.StringSlice(parseMultiSelectValue(instanceId)) instances, err := e.ec2DescribeInstances(ctx, pluginCtx, region, nil, instanceIds) if err != nil { return nil, err @@ -76,16 +72,16 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []ec2types.Filter + var filters []*ec2.Filter for k, v := range filterMap { if vv, ok := v.([]any); ok { - var values []string + var values []*string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, vvvv) + values = append(values, &vvvv) } } - filters = append(filters, ec2types.Filter{ + filters = append(filters, &ec2.Filter{ Name: aws.String(k), Values: values, }) @@ -124,7 +120,7 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return result, nil } -func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) (value string, found bool, err error) { +func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (value string, found bool, err error) { tags := make(map[string]string) for _, tag := range instance.Tags { tags[*tag.Key] = *tag.Value @@ -156,12 +152,7 @@ func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) if v.Kind() == reflect.Ptr && v.IsNil() { return "", false, nil } - if v.Kind() == reflect.String { - if v.String() == "" { - return "", false, nil - } - data = v.String() - } else if attr, ok := v.Interface().(*string); ok { + if attr, ok := v.Interface().(*string); ok { data = *attr } else if attr, ok := v.Interface().(*time.Time); ok { data = attr.String() @@ -188,23 +179,24 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []resourcegroupstaggingapitypes.TagFilter + var filters []*resourcegroupstaggingapi.TagFilter for k, v := range tagsMap { if vv, ok := v.([]any); ok { - var values []string + var values []*string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, vvvv) + values = append(values, &vvvv) } } - filters = append(filters, resourcegroupstaggingapitypes.TagFilter{ + filters = append(filters, &resourcegroupstaggingapi.TagFilter{ Key: aws.String(k), Values: values, }) } } - resourceTypes := []string{resourceType} + var resourceTypes []*string + resourceTypes = append(resourceTypes, &resourceType) resources, err := e.resourceGroupsGetResources(ctx, pluginCtx, region, filters, resourceTypes) if err != nil { @@ -220,7 +212,7 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return result, nil } -func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []ec2types.Filter, instanceIds []string) (*ec2.DescribeInstancesOutput, error) { +func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) { params := &ec2.DescribeInstancesInput{ Filters: filters, InstanceIds: instanceIds, @@ -231,20 +223,19 @@ func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx return nil, err } - resp := &ec2.DescribeInstancesOutput{} - pager := ec2.NewDescribeInstancesPaginator(client, params) - for pager.HasMorePages() { - page, err := pager.NextPage(ctx) - if err != nil { - return resp, fmt.Errorf("describe instances pager failed: %w", err) - } + var resp ec2.DescribeInstancesOutput + if err := client.DescribeInstancesPagesWithContext(ctx, params, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { resp.Reservations = append(resp.Reservations, page.Reservations...) + return !lastPage + }); err != nil { + return nil, fmt.Errorf("failed to call ec2:DescribeInstances, %w", err) } - return resp, nil + + return &resp, nil } -func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []resourcegroupstaggingapitypes.TagFilter, - resourceTypes []string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { +func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*resourcegroupstaggingapi.TagFilter, + resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { params := &resourcegroupstaggingapi.GetResourcesInput{ ResourceTypeFilters: resourceTypes, TagFilters: filters, @@ -256,13 +247,12 @@ func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, plu } var resp resourcegroupstaggingapi.GetResourcesOutput - paginator := resourcegroupstaggingapi.NewGetResourcesPaginator(client, params) - for paginator.HasMorePages() { - page, err := paginator.NextPage(ctx) - if err != nil { - return nil, fmt.Errorf("get resource groups paginator failed: %w", err) - } - resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) + if err := client.GetResourcesPagesWithContext(ctx, params, + func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool { + resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) + return !lastPage + }); err != nil { + return nil, fmt.Errorf("failed to call tag:GetResources, %w", err) } return &resp, nil @@ -280,17 +270,17 @@ func (e *cloudWatchExecutor) handleGetLogGroups(ctx context.Context, pluginCtx b } logGroupLimit := defaultLogGroupLimit - intLimit, err := strconv.ParseInt(limit, 10, 32) + intLimit, err := strconv.ParseInt(limit, 10, 64) if err == nil && intLimit > 0 { - logGroupLimit = int32(intLimit) + logGroupLimit = intLimit } - input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(logGroupLimit)} + input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(logGroupLimit)} if len(logGroupNamePrefix) > 0 { input.LogGroupNamePrefix = aws.String(logGroupNamePrefix) } var response *cloudwatchlogs.DescribeLogGroupsOutput - response, err = logsClient.DescribeLogGroups(ctx, input) + response, err = logsClient.DescribeLogGroupsWithContext(ctx, input) if err != nil || response == nil { return nil, err } diff --git a/pkg/tsdb/cloudwatch/metric_find_query_test.go b/pkg/tsdb/cloudwatch/metric_find_query_test.go index 7a4598896ab..85329bd5760 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query_test.go +++ b/pkg/tsdb/cloudwatch/metric_find_query_test.go @@ -6,12 +6,14 @@ import ( "net/url" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -19,26 +21,26 @@ import ( ) func TestQuery_InstanceAttributes(t *testing.T) { - origNewEC2API := NewEC2API + origNewEC2Client := NewEC2Client t.Cleanup(func() { - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var cli oldEC2Client - NewEC2API = func(aws.Config) models.EC2APIProvider { + NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { return cli } t.Run("Get instance ID", func(t *testing.T) { const instanceID = "i-12345678" cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String(instanceID), - Tags: []ec2types.Tag{ + Tags: []*ec2.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -50,7 +52,9 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) filterMap := map[string][]string{ "tag:Environment": {"production"}, @@ -78,17 +82,17 @@ func TestQuery_InstanceAttributes(t *testing.T) { }) t.Run("Get different types", func(t *testing.T) { - var expectedInt int32 = 3 + var expectedInt int64 = 3 var expectedBool = true var expectedArn = "arn" cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { AmiLaunchIndex: &expectedInt, EbsOptimized: &expectedBool, - IamInstanceProfile: &ec2types.IamInstanceProfile{ + IamInstanceProfile: &ec2.IamInstanceProfile{ Arn: &expectedArn, }, }, @@ -97,7 +101,9 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -157,52 +163,52 @@ func TestQuery_InstanceAttributes(t *testing.T) { } func TestQuery_EBSVolumeIDs(t *testing.T) { - origNewEC2API := NewEC2API + origNewEC2Client := NewEC2Client t.Cleanup(func() { - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var cli oldEC2Client - NewEC2API = func(aws.Config) models.EC2APIProvider { + NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { return cli } t.Run("", func(t *testing.T) { cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String("i-1"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, }, }, { InstanceId: aws.String("i-2"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, }, }, }, }, { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String("i-3"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, }, }, { InstanceId: aws.String("i-4"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, }, }, }, @@ -210,7 +216,9 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.handleGetEbsVolumeIds( @@ -234,23 +242,23 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { } func TestQuery_ResourceARNs(t *testing.T) { - origNewRGTAClient := NewRGTAClient + origNewRGTAClient := newRGTAClient t.Cleanup(func() { - NewRGTAClient = origNewRGTAClient + newRGTAClient = origNewRGTAClient }) var cli fakeRGTAClient - NewRGTAClient = func(aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { + newRGTAClient = func(client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { return cli } t.Run("", func(t *testing.T) { cli = fakeRGTAClient{ - tagMapping: []resourcegroupstaggingapitypes.ResourceTagMapping{ + tagMapping: []*resourcegroupstaggingapi.ResourceTagMapping{ { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567"), - Tags: []resourcegroupstaggingapitypes.Tag{ + Tags: []*resourcegroupstaggingapi.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -259,7 +267,7 @@ func TestQuery_ResourceARNs(t *testing.T) { }, { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321"), - Tags: []resourcegroupstaggingapitypes.Tag{ + Tags: []*resourcegroupstaggingapi.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -269,7 +277,9 @@ func TestQuery_ResourceARNs(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) tagMap := map[string][]string{ "Environment": {"production"}, diff --git a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go index 86040e165a2..cdb1aeca82f 100644 --- a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go +++ b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go @@ -1,65 +1,71 @@ package mocks import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/stretchr/testify/mock" ) type FakeMetricsAPI struct { - models.CWClient - - Metrics []cloudwatchtypes.Metric - OwningAccounts []string + Metrics []*cloudwatch.Metric + OwningAccounts []*string MetricsPerPage int - - cursor int } -func (c *FakeMetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { +func (c *FakeMetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { if c.MetricsPerPage == 0 { c.MetricsPerPage = 1000 } - var metrics []cloudwatchtypes.Metric - nextToken := aws.String("yes") - if c.cursor < len(c.Metrics) { - end := c.cursor + c.MetricsPerPage - if end > len(c.Metrics) { - end = len(c.Metrics) - nextToken = nil + chunks := chunkSlice(c.Metrics, c.MetricsPerPage) + + for i, metrics := range chunks { + response := fn(&cloudwatch.ListMetricsOutput{ + Metrics: metrics, + OwningAccounts: c.OwningAccounts, + }, i+1 == len(chunks)) + if !response { + break } - metrics = c.Metrics[c.cursor:end] } - c.cursor += c.MetricsPerPage + return nil +} - return &cloudwatch.ListMetricsOutput{ - Metrics: metrics, - OwningAccounts: c.OwningAccounts, - NextToken: nextToken, - }, nil +func chunkSlice(slice []*cloudwatch.Metric, chunkSize int) [][]*cloudwatch.Metric { + var chunks [][]*cloudwatch.Metric + for { + if len(slice) == 0 { + break + } + if len(slice) < chunkSize { + chunkSize = len(slice) + } + + chunks = append(chunks, slice[0:chunkSize]) + slice = slice[chunkSize:] + } + + return chunks } type MetricsAPI struct { + cloudwatchiface.CloudWatchAPI mock.Mock - models.CWClient - Metrics []cloudwatchtypes.Metric + Metrics []*cloudwatch.Metric } -func (m *MetricsAPI) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *MetricsAPI) GetMetricDataWithContext(ctx aws.Context, input *cloudwatch.GetMetricDataInput, opts ...request.Option) (*cloudwatch.GetMetricDataOutput, error) { + args := m.Called(ctx, input, opts) return args.Get(0).(*cloudwatch.GetMetricDataOutput), args.Error(1) } -func (m *MetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return &cloudwatch.ListMetricsOutput{ +func (m *MetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { + fn(&cloudwatch.ListMetricsOutput{ Metrics: m.Metrics, - }, m.Called().Error(0) + }, true) + + return m.Called().Error(0) } diff --git a/pkg/tsdb/cloudwatch/mocks/logs.go b/pkg/tsdb/cloudwatch/mocks/logs.go index 7843fdaacd1..e0b16bf436d 100644 --- a/pkg/tsdb/cloudwatch/mocks/logs.go +++ b/pkg/tsdb/cloudwatch/mocks/logs.go @@ -3,7 +3,10 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -13,13 +16,13 @@ type LogsAPI struct { mock.Mock } -func (l *LogsAPI) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (l *LogsAPI) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.DescribeLogGroupsOutput), args.Error(1) } -func (l *LogsAPI) GetLogGroupFields(_ context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (l *LogsAPI) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.GetLogGroupFieldsOutput), args.Error(1) @@ -29,40 +32,26 @@ type LogsService struct { mock.Mock } -func (l *LogsService) GetLogGroups(_ context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (l *LogsService) GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroup]), args.Error(1) } -func (l *LogsService) GetLogGroupFields(_ context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (l *LogsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroupField]), args.Error(1) } type MockLogEvents struct { - mock.Mock -} + cloudwatchlogsiface.CloudWatchLogsAPI -func (m *MockLogEvents) StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - return nil, nil + mock.Mock } -func (m *MockLogEvents) GetLogEvents(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *MockLogEvents) GetLogEventsWithContext(ctx aws.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.GetLogEventsOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/metrics_client.go b/pkg/tsdb/cloudwatch/mocks/metrics_client.go index adf23796945..44ab1fbcb5b 100644 --- a/pkg/tsdb/cloudwatch/mocks/metrics_client.go +++ b/pkg/tsdb/cloudwatch/mocks/metrics_client.go @@ -3,8 +3,7 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -13,7 +12,7 @@ type FakeMetricsClient struct { mock.Mock } -func (m *FakeMetricsClient) ListMetricsWithPageLimit(_ context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { +func (m *FakeMetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { args := m.Called(params) return args.Get(0).([]resources.MetricResponse), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/oam_client.go b/pkg/tsdb/cloudwatch/mocks/oam_client.go index d16253e3252..932ca1579b4 100644 --- a/pkg/tsdb/cloudwatch/mocks/oam_client.go +++ b/pkg/tsdb/cloudwatch/mocks/oam_client.go @@ -3,7 +3,8 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/oam" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/oam" "github.com/stretchr/testify/mock" ) @@ -11,12 +12,12 @@ type FakeOAMClient struct { mock.Mock } -func (o *FakeOAMClient) ListSinks(_ context.Context, input *oam.ListSinksInput, _ ...func(*oam.Options)) (*oam.ListSinksOutput, error) { +func (o *FakeOAMClient) ListSinksWithContext(ctx context.Context, input *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListSinksOutput), args.Error(1) } -func (o *FakeOAMClient) ListAttachedLinks(_ context.Context, input *oam.ListAttachedLinksInput, _ ...func(*oam.Options)) (*oam.ListAttachedLinksOutput, error) { +func (o *FakeOAMClient) ListAttachedLinksWithContext(ctx context.Context, input *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListAttachedLinksOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/regions.go b/pkg/tsdb/cloudwatch/mocks/regions.go index a7717481516..1d0ff115ea7 100644 --- a/pkg/tsdb/cloudwatch/mocks/regions.go +++ b/pkg/tsdb/cloudwatch/mocks/regions.go @@ -3,7 +3,9 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -12,7 +14,7 @@ type RegionsService struct { mock.Mock } -func (r *RegionsService) GetRegions(_ context.Context) (in []resources.ResourceResponse[resources.Region], e error) { +func (r *RegionsService) GetRegions(ctx context.Context) (in []resources.ResourceResponse[resources.Region], e error) { args := r.Called() return args.Get(0).(([]resources.ResourceResponse[resources.Region])), args.Error(1) } @@ -21,12 +23,12 @@ type EC2Mock struct { mock.Mock } -func (e *EC2Mock) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { +func (e *EC2Mock) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) { args := e.Called() return args.Get(0).(*ec2.DescribeRegionsOutput), args.Error(1) } -func (e *EC2Mock) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { - args := e.Called(in) - return nil, args.Error(0) +func (e *EC2Mock) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { + args := e.Called(in, fn) + return args.Error(0) } diff --git a/pkg/tsdb/cloudwatch/models/api.go b/pkg/tsdb/cloudwatch/models/api.go index 35d60b7febc..c54c156b123 100644 --- a/pkg/tsdb/cloudwatch/models/api.go +++ b/pkg/tsdb/cloudwatch/models/api.go @@ -4,11 +4,11 @@ import ( "context" "net/url" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/oam" - + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -19,13 +19,12 @@ type RequestContextFactoryFunc func(ctx context.Context, pluginCtx backend.Plugi type RouteHandlerFunc func(ctx context.Context, pluginCtx backend.PluginContext, reqContextFactory RequestContextFactoryFunc, parameters url.Values) ([]byte, *HttpError) type RequestContext struct { - MetricsClientProvider MetricsClientProvider - ListMetricsAPIProvider cloudwatch.ListMetricsAPIClient - LogsAPIProvider CloudWatchLogsAPIProvider - OAMAPIProvider OAMAPIProvider - EC2APIProvider EC2APIProvider - Settings CloudWatchSettings - Logger log.Logger + MetricsClientProvider MetricsClientProvider + LogsAPIProvider CloudWatchLogsAPIProvider + OAMAPIProvider OAMAPIProvider + EC2APIProvider EC2APIProvider + Settings CloudWatchSettings + Logger log.Logger } // Services @@ -36,8 +35,8 @@ type ListMetricsProvider interface { } type LogGroupsProvider interface { - GetLogGroups(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) - GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) + GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) + GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) } type AccountsProvider interface { @@ -55,42 +54,20 @@ type MetricsClientProvider interface { // APIs - instead of using the API defined in the services within the aws-sdk-go directly, specify a subset of the API with methods that are actually used in a service or a client type CloudWatchMetricsAPIProvider interface { - ListMetrics(ctx context.Context, in *cloudwatch.ListMetricsInput, optFns ...func(*cloudwatch.Options)) error + ListMetricsPagesWithContext(ctx context.Context, in *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error } type CloudWatchLogsAPIProvider interface { - cloudwatchlogs.DescribeLogGroupsAPIClient - GetLogGroupFields(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) + DescribeLogGroupsWithContext(ctx context.Context, in *cloudwatchlogs.DescribeLogGroupsInput, opts ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) + GetLogGroupFieldsWithContext(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) } type OAMAPIProvider interface { - ListSinks(ctx context.Context, in *oam.ListSinksInput, optFns ...func(options *oam.Options)) (*oam.ListSinksOutput, error) - ListAttachedLinks(ctx context.Context, in *oam.ListAttachedLinksInput, optFns ...func(options *oam.Options)) (*oam.ListAttachedLinksOutput, error) + ListSinksWithContext(ctx context.Context, in *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) + ListAttachedLinksWithContext(ctx context.Context, in *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) } type EC2APIProvider interface { - DescribeRegions(ctx context.Context, in *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) - ec2.DescribeInstancesAPIClient -} - -type CWLogsClient interface { - StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) - StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) - GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) - - cloudwatchlogs.GetLogEventsAPIClient - cloudwatchlogs.DescribeLogGroupsAPIClient -} - -type CWClient interface { - AlarmsAPI - cloudwatch.GetMetricDataAPIClient - cloudwatch.ListMetricsAPIClient -} - -type AlarmsAPI interface { - cloudwatch.DescribeAlarmsAPIClient - cloudwatch.DescribeAlarmHistoryAPIClient - - DescribeAlarmsForMetric(context.Context, *cloudwatch.DescribeAlarmsForMetricInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) + DescribeRegionsWithContext(ctx context.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) + DescribeInstancesPagesWithContext(ctx context.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go index 301b2d59569..6907257c55e 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go @@ -11,8 +11,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/google/uuid" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -202,11 +201,7 @@ func (q *CloudWatchQuery) BuildDeepLink(startTime time.Time, endTime time.Time) return "", fmt.Errorf("could not marshal link: %w", err) } - endpoint, err := getEndpoint(q.Region) - if err != nil { - return "", err - } - url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, endpoint)) + url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, getEndpoint(q.Region))) if err != nil { return "", fmt.Errorf("unable to parse CloudWatch console deep link") } @@ -508,18 +503,14 @@ func parseDimensions(dimensions dataquery.Dimensions) (map[string][]string, erro return parsedDimensions, nil } -func getEndpoint(region string) (string, error) { - resolver := cloudwatch.NewDefaultEndpointResolver() - endpoint, err := resolver.ResolveEndpoint(region, cloudwatch.EndpointResolverOptions{}) - if err != nil { - return "", fmt.Errorf("resolve endpoint failed: %w", err) +func getEndpoint(region string) string { + partition, _ := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region) + url := defaultConsoleURL + if partition.ID() == endpoints.AwsUsGovPartitionID { + url = usGovConsoleURL } - consoleURL := defaultConsoleURL - switch endpoint.PartitionID { - case "aws-us-gov": - consoleURL = usGovConsoleURL - case "aws-cn": - consoleURL = chinaConsoleURL + if partition.ID() == endpoints.AwsCnPartitionID { + url = chinaConsoleURL } - return fmt.Sprintf("%s.%s", region, consoleURL), nil + return fmt.Sprintf("%s.%s", region, url) } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go index d38fdd9c659..fe438fb2afd 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go @@ -7,15 +7,13 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) var logger = log.NewNullLogger() @@ -934,6 +932,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create for name, tc := range testCases { t.Run(name, func(t *testing.T) { average := "Average" + false := false queryToMigrate := metricsDataQuery{ CloudWatchMetricsQuery: dataquery.CloudWatchMetricsQuery{ @@ -946,7 +945,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create }, Statistic: &average, Period: utils.Pointer("600"), - Hide: aws.Bool(false), + Hide: &false, }, } @@ -1306,8 +1305,7 @@ func TestGetEndpoint(t *testing.T) { } for _, ts := range testcases { t.Run(fmt.Sprintf("should create correct endpoint for %s", ts), func(t *testing.T) { - actual, err := getEndpoint(ts.region) - assert.NoError(t, err) + actual := getEndpoint(ts.region) assert.Equal(t, ts.expectedEndpoint, actual) }) } diff --git a/pkg/tsdb/cloudwatch/models/logs_query.go b/pkg/tsdb/cloudwatch/models/logs_query.go index 49b9e1187b1..f9ea0c3e0ff 100644 --- a/pkg/tsdb/cloudwatch/models/logs_query.go +++ b/pkg/tsdb/cloudwatch/models/logs_query.go @@ -8,7 +8,7 @@ type LogsQuery struct { dataquery.CloudWatchLogsQuery StartTime *int64 EndTime *int64 - Limit *int32 + Limit *int64 LogGroupName string LogStreamName string QueryId string diff --git a/pkg/tsdb/cloudwatch/models/query_row_response.go b/pkg/tsdb/cloudwatch/models/query_row_response.go index 8362faf89df..b99913b0a91 100644 --- a/pkg/tsdb/cloudwatch/models/query_row_response.go +++ b/pkg/tsdb/cloudwatch/models/query_row_response.go @@ -1,32 +1,32 @@ package models import ( - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/service/cloudwatch" ) -// QueryRowResponse represents the GetMetricData response for a query row in the query editor. +// queryRowResponse represents the GetMetricData response for a query row in the query editor. type QueryRowResponse struct { - partialDataSet map[string]*cloudwatchtypes.MetricDataResult + partialDataSet map[string]*cloudwatch.MetricDataResult ErrorCodes map[string]bool HasArithmeticError bool ArithmeticErrorMessage string HasPermissionError bool PermissionErrorMessage string - Metrics []*cloudwatchtypes.MetricDataResult - StatusCode cloudwatchtypes.StatusCode + Metrics []*cloudwatch.MetricDataResult + StatusCode string } func NewQueryRowResponse(errors map[string]bool) QueryRowResponse { return QueryRowResponse{ - partialDataSet: make(map[string]*cloudwatchtypes.MetricDataResult), + partialDataSet: make(map[string]*cloudwatch.MetricDataResult), ErrorCodes: errors, HasArithmeticError: false, ArithmeticErrorMessage: "", - Metrics: []*cloudwatchtypes.MetricDataResult{}, + Metrics: []*cloudwatch.MetricDataResult{}, } } -func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataResult) { +func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) { if mdr.Label == nil { return } @@ -34,16 +34,16 @@ func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataRe if partialData, ok := q.partialDataSet[*mdr.Label]; ok { partialData.Timestamps = append(partialData.Timestamps, mdr.Timestamps...) partialData.Values = append(partialData.Values, mdr.Values...) - q.StatusCode = mdr.StatusCode - if mdr.StatusCode != cloudwatchtypes.StatusCodePartialData { + q.StatusCode = *mdr.StatusCode + if *mdr.StatusCode != "PartialData" { delete(q.partialDataSet, *mdr.Label) } return } q.Metrics = append(q.Metrics, mdr) - q.StatusCode = mdr.StatusCode - if mdr.StatusCode == cloudwatchtypes.StatusCodePartialData { + q.StatusCode = *mdr.StatusCode + if *mdr.StatusCode == "PartialData" { q.partialDataSet[*mdr.Label] = mdr } } diff --git a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go index 45474dbfa41..80843c9d74e 100644 --- a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go +++ b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go @@ -6,11 +6,11 @@ import ( "strconv" ) -const defaultLogGroupLimit = int32(50) +const defaultLogGroupLimit = int64(50) type LogGroupsRequest struct { ResourceRequest - Limit int32 + Limit int64 LogGroupNamePrefix, LogGroupNamePattern *string ListAllLogGroups bool } @@ -45,11 +45,11 @@ func setIfNotEmptyString(paramValue string) *string { return ¶mValue } -func getLimit(limit string) int32 { +func getLimit(limit string) int64 { logGroupLimit := defaultLogGroupLimit intLimit, err := strconv.ParseInt(limit, 10, 64) if err == nil && intLimit > 0 { - logGroupLimit = int32(intLimit) + logGroupLimit = intLimit } return logGroupLimit } diff --git a/pkg/tsdb/cloudwatch/models/resources/types.go b/pkg/tsdb/cloudwatch/models/resources/types.go index f5de61f7f27..baeab0c04b5 100644 --- a/pkg/tsdb/cloudwatch/models/resources/types.go +++ b/pkg/tsdb/cloudwatch/models/resources/types.go @@ -1,8 +1,6 @@ package resources -import ( - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" -) +import "github.com/aws/aws-sdk-go/service/cloudwatch" type Dimension struct { Name string @@ -15,7 +13,7 @@ type ResourceResponse[T any] struct { } type MetricResponse struct { - Metric cloudwatchtypes.Metric + *cloudwatch.Metric AccountId *string `json:"accountId,omitempty"` } diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 2d7db092e48..85b69a00a8b 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -8,8 +8,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" @@ -89,7 +88,7 @@ func aggregateResponse(getMetricDataOutputs []*cloudwatch.GetMetricDataOutput) m } } - response.AddMetricDataResult(&r) + response.AddMetricDataResult(r) responseByID[id] = response } } @@ -229,9 +228,16 @@ func buildDataFrames(ctx context.Context, aggregatedResponse models.QueryRowResp } else { labels = getLabels(label, query, false) } + timestamps := []*time.Time{} + points := []*float64{} + for j, t := range metric.Timestamps { + val := metric.Values[j] + timestamps = append(timestamps, t) + points = append(points, val) + } - timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, metric.Timestamps) - valueField := data.NewField(data.TimeSeriesValueFieldName, labels, metric.Values) + timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps) + valueField := data.NewField(data.TimeSeriesValueFieldName, labels, points) // CloudWatch appends the dimensions to the returned label if the query label is not dynamic, so static labels need to be set if hasStaticLabel { diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index 3e46cd6c861..d1c8229fa3e 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -7,10 +7,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -43,7 +41,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 10) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) + assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) }) t.Run("should have exceeded request limit", func(t *testing.T) { assert.True(t, aggregatedResponse[idA].ErrorCodes["MaxMetricsExceeded"]) @@ -65,7 +63,7 @@ func TestCloudWatchResponseParser(t *testing.T) { aggregatedResponse := aggregateResponse(getMetricDataOutputs) idB := "b" t.Run("should have statuscode is 'PartialData'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodePartialData, aggregatedResponse[idB].StatusCode) + assert.Equal(t, "PartialData", aggregatedResponse[idB].StatusCode) }) t.Run("should have an arithmetic error and an error message", func(t *testing.T) { assert.True(t, aggregatedResponse[idB].HasArithmeticError) @@ -87,7 +85,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 6) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) + assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) }) }) @@ -155,36 +153,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multi filter", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("lb1|&|lb1"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, { Id: aws.String("id2"), Label: aws.String("lb2|&|lb2"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -225,36 +223,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multiple wildcard filters", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label lb3|&|inst1|&|balancer 1"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, { Id: aws.String("lb4"), Label: aws.String("some label lb4|&|inst2|&|balancer 2"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -296,17 +294,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{}, + StatusCode: aws.String("Complete"), }, }, } @@ -339,17 +337,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{}, + StatusCode: aws.String("Complete"), }, }, } @@ -389,15 +387,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when not using multi-value dimension filters on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -431,15 +429,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when non-static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -474,15 +472,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -517,15 +515,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when code editor used for `MetricSearch` query add fallback label", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -555,24 +553,24 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no label set and `GROUP BY` clause has multiple fields", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("EC2 vCPU"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, { Id: aws.String("query2"), Label: aws.String("Elastic Loading Balancing ApplicationLoadBalancersPerRegion"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -603,15 +601,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no `GROUP BY` clause", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("cloudwatch-default-label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -637,15 +635,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("ignore dimensions for raw mode query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -677,21 +675,21 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("Parse cloudwatch response", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -719,9 +717,9 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { assert.Equal(t, "some label", frame.Name) assert.Equal(t, "Time", frame.Fields[0].Name) assert.Equal(t, "lb", frame.Fields[1].Labels["LoadBalancer"]) - assert.Equal(t, 10.0, frame.Fields[1].At(0).(float64)) - assert.Equal(t, 20.0, frame.Fields[1].At(1).(float64)) - assert.Equal(t, 30.0, frame.Fields[1].At(2).(float64)) + assert.Equal(t, 10.0, *frame.Fields[1].At(0).(*float64)) + assert.Equal(t, 20.0, *frame.Fields[1].At(1).(*float64)) + assert.Equal(t, 30.0, *frame.Fields[1].At(2).(*float64)) assert.Equal(t, "Value", frame.Fields[1].Name) assert.Equal(t, "", frame.Fields[1].Config.DisplayName) }) diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields.go b/pkg/tsdb/cloudwatch/routes/log_group_fields.go index 418e234cbbe..455c2860368 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields.go @@ -22,7 +22,7 @@ func LogGroupFieldsHandler(ctx context.Context, pluginCtx backend.PluginContext, return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroupFields, err := service.GetLogGroupFields(ctx, request) + logGroupFields, err := service.GetLogGroupFieldsWithContext(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroupFields error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go index 382244cacd3..e1c1de40eb4 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go @@ -31,7 +31,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns 500 if GetLogGroupFields method fails", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) + mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -47,7 +47,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns valid json response if everything is ok", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ + mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ { AccountId: new(string), Value: resources.LogGroupField{ diff --git a/pkg/tsdb/cloudwatch/routes/log_groups.go b/pkg/tsdb/cloudwatch/routes/log_groups.go index 15f5c6d3fd8..34c9f35ab01 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups.go @@ -24,7 +24,7 @@ func LogGroupsHandler(ctx context.Context, pluginCtx backend.PluginContext, reqC return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroups, err := service.GetLogGroups(ctx, request) + logGroups, err := service.GetLogGroupsWithContext(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroups error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_groups_test.go b/pkg/tsdb/cloudwatch/routes/log_groups_test.go index 7e1b264a128..b1f9e53037e 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups_test.go @@ -7,10 +7,10 @@ import ( "net/http/httptest" "testing" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -29,7 +29,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns 1 log group with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ Value: resources.LogGroup{ Arn: "some arn", Name: "some name", @@ -51,7 +51,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns multiple log groups with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return( + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return( []resources.ResourceResponse[resources.LogGroup]{ { Value: resources.LogGroup{ @@ -97,7 +97,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error when both logGroupPrefix and logGroup Pattern are provided", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -113,7 +113,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix, accountId, and logGroupPattern", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -123,7 +123,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{}, LogGroupNamePrefix: nil, @@ -133,7 +133,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix when both are absent", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -143,7 +143,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: nil, }) @@ -151,7 +151,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes log group limit from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -161,14 +161,14 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 2, }) }) t.Run("passes logGroupPrefix from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -178,7 +178,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: utils.Pointer("some-prefix"), }) @@ -186,7 +186,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -196,7 +196,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePattern: utils.Pointer("some-pattern"), }) @@ -204,7 +204,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -214,7 +214,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("some-account-id")}, }) @@ -222,7 +222,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error if service returns error", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything). + mockLogsService.On("GetLogGroupsWithContext", mock.Anything). Return([]resources.ResourceResponse[resources.LogGroup]{}, fmt.Errorf("some error")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil diff --git a/pkg/tsdb/cloudwatch/services/accounts.go b/pkg/tsdb/cloudwatch/services/accounts.go index 94691d258ee..f05b18335a3 100644 --- a/pkg/tsdb/cloudwatch/services/accounts.go +++ b/pkg/tsdb/cloudwatch/services/accounts.go @@ -4,10 +4,9 @@ import ( "context" "errors" "fmt" - "strings" - oam "github.com/aws/aws-sdk-go-v2/service/oam" - oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -24,14 +23,20 @@ func NewAccountsService(oamClient models.OAMAPIProvider) models.AccountsProvider func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ([]resources.ResourceResponse[resources.Account], error) { var nextToken *string - sinks := []oamtypes.ListSinksItem{} + sinks := []*oam.ListSinksItem{} for { - response, err := a.ListSinks(ctx, &oam.ListSinksInput{NextToken: nextToken}) + response, err := a.ListSinksWithContext(ctx, &oam.ListSinksInput{NextToken: nextToken}) if err != nil { - // TODO: this is a bit hacky, figure out how to do it right in v2 - if strings.Contains(err.Error(), "AccessDeniedException") { - return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, err.Error()) + var aerr awserr.Error + if errors.As(err, &aerr) { + switch aerr.Code() { + // unlike many other services, OAM doesn't define this error code. however, it's returned in case calling role/user has insufficient permissions + case "AccessDeniedException": + return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, aerr.Message()) + } } + } + if err != nil { return nil, fmt.Errorf("ListSinks error: %w", err) } @@ -57,7 +62,7 @@ func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ( nextToken = nil for { - links, err := a.ListAttachedLinks(ctx, &oam.ListAttachedLinksInput{ + links, err := a.ListAttachedLinksWithContext(ctx, &oam.ListAttachedLinksInput{ SinkIdentifier: sinkIdentifier, NextToken: nextToken, }) diff --git a/pkg/tsdb/cloudwatch/services/accounts_test.go b/pkg/tsdb/cloudwatch/services/accounts_test.go index ba75ad4a9a0..dbbadaf660c 100644 --- a/pkg/tsdb/cloudwatch/services/accounts_test.go +++ b/pkg/tsdb/cloudwatch/services/accounts_test.go @@ -2,14 +2,12 @@ package services import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/oam" - oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/assert" @@ -20,20 +18,21 @@ import ( func TestHandleGetAccounts(t *testing.T) { t.Run("Should return an error in case of insufficient permissions from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, errors.New("AccessDeniedException")) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, awserr.New("AccessDeniedException", + "AWS message", nil)) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.Error(t, err) assert.Nil(t, resp) - assert.Equal(t, "access denied. please check your IAM policy: AccessDeniedException", err.Error()) + assert.Equal(t, err.Error(), "access denied. please check your IAM policy: AWS message") assert.ErrorIs(t, err, ErrAccessDeniedException) }) t.Run("Should return an error in case of any error from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -45,7 +44,7 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return empty array in case no monitoring account exists", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, nil) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -56,26 +55,26 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return one monitoring account (the first) even though ListSinks returns multiple sinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) require.Len(t, resp, 1) assert.True(t, resp[0].Value.IsMonitoringAccount) assert.Equal(t, "Account 1", resp[0].Value.Label) @@ -84,28 +83,28 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should merge the first sink with attached links", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []oamtypes.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []*oam.ListAttachedLinksItem{ {Label: aws.String("Account 10"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10")}, {Label: aws.String("Account 11"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789014:log-group:my-log-group11")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []oamtypes.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []*oam.ListAttachedLinksItem{ {Label: aws.String("Account 12"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group12")}, }, NextToken: nil, @@ -115,8 +114,8 @@ func TestHandleGetAccounts(t *testing.T) { resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) - fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinks", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinksWithContext", 2) expectedAccounts := []resources.ResourceResponse[resources.Account]{ {Value: resources.Account{Id: "123456789012", Label: "Account 1", Arn: "arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1", IsMonitoringAccount: true}}, {Value: resources.Account{Id: "123456789013", Label: "Account 10", Arn: "arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10", IsMonitoringAccount: false}}, @@ -128,34 +127,34 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should call ListAttachedLinks with arn of first sink", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil).Once() - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) _, _ = accounts.GetAccountsForCurrentUserOrRole(context.Background()) - fakeOAMClient.AssertCalled(t, "ListAttachedLinks", &oam.ListAttachedLinksInput{ + fakeOAMClient.AssertCalled(t, "ListAttachedLinksWithContext", &oam.ListAttachedLinksInput{ SinkIdentifier: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1"), }) }) t.Run("Should return an error in case of any error from ListAttachedLinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) diff --git a/pkg/tsdb/cloudwatch/services/list_metrics.go b/pkg/tsdb/cloudwatch/services/list_metrics.go index e702cc025d1..3f6088ce32b 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics.go @@ -5,10 +5,8 @@ import ( "fmt" "sort" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -32,7 +30,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } @@ -40,8 +38,8 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte response := []resources.ResourceResponse[string]{} // remove duplicates dupCheck := make(map[string]struct{}) - for _, accountMetric := range accountMetrics { - for _, dim := range accountMetric.Metric.Dimensions { + for _, metric := range metrics { + for _, dim := range metric.Dimensions { if _, exists := dupCheck[*dim.Name]; exists { continue } @@ -60,7 +58,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte } dupCheck[*dim.Name] = struct{}{} - response = append(response, resources.ResourceResponse[string]{AccountId: accountMetric.AccountId, Value: *dim.Name}) + response = append(response, resources.ResourceResponse[string]{AccountId: metric.AccountId, Value: *dim.Name}) } } @@ -75,15 +73,15 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } response := []resources.ResourceResponse[string]{} dupCheck := make(map[string]bool) - for _, metric := range accountMetrics { - for _, dim := range metric.Metric.Dimensions { + for _, metric := range metrics { + for _, dim := range metric.Dimensions { if *dim.Name == r.DimensionKey { if _, exists := dupCheck[*dim.Value]; exists { continue @@ -104,19 +102,19 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error) { input := &cloudwatch.ListMetricsInput{Namespace: aws.String(r.Namespace)} setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, err } response := []resources.ResourceResponse[resources.Metric]{} dupCheck := make(map[string]struct{}) - for _, accountMetric := range accountMetrics { - if _, exists := dupCheck[*accountMetric.Metric.MetricName]; exists { + for _, metric := range metrics { + if _, exists := dupCheck[*metric.MetricName]; exists { continue } - dupCheck[*accountMetric.Metric.MetricName] = struct{}{} - response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: accountMetric.AccountId, Value: resources.Metric{Name: *accountMetric.Metric.MetricName, Namespace: *accountMetric.Metric.Namespace}}) + dupCheck[*metric.MetricName] = struct{}{} + response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: metric.AccountId, Value: resources.Metric{Name: *metric.MetricName, Namespace: *metric.Namespace}}) } return response, nil @@ -124,7 +122,7 @@ func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resour func setDimensionFilter(input *cloudwatch.ListMetricsInput, dimensionFilter []*resources.Dimension) { for _, dimension := range dimensionFilter { - df := cloudwatchtypes.DimensionFilter{ + df := &cloudwatch.DimensionFilter{ Name: aws.String(dimension.Name), } if dimension.Value != "" { diff --git a/pkg/tsdb/cloudwatch/services/list_metrics_test.go b/pkg/tsdb/cloudwatch/services/list_metrics_test.go index daf5c9f458b..7f832878443 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics_test.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics_test.go @@ -4,10 +4,8 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -20,20 +18,20 @@ const useLinkedAccountsId = "all" var metricResponse = []resources.MetricResponse{ { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-1234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, }, }, }, { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-5234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg")}, @@ -41,10 +39,10 @@ var metricResponse = []resources.MetricResponse{ }, }, { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-64234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t3.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg2")}, @@ -88,7 +86,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -103,7 +101,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, @@ -116,7 +114,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { MetricName: "", DimensionFilter: []*resources.Dimension{{Name: "InstanceId", Value: ""}}, }, - listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}}, + listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}}, }, } @@ -165,7 +163,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -180,7 +178,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, diff --git a/pkg/tsdb/cloudwatch/services/log_groups.go b/pkg/tsdb/cloudwatch/services/log_groups.go index ad0011b6456..1bbc99a67bd 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups.go +++ b/pkg/tsdb/cloudwatch/services/log_groups.go @@ -3,9 +3,9 @@ package services import ( "context" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -20,9 +20,9 @@ func NewLogGroupsService(logsClient models.CloudWatchLogsAPIProvider, isCrossAcc return &LogGroupsService{logGroupsAPI: logsClient, isCrossAccountEnabled: isCrossAccountEnabled} } -func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (s *LogGroupsService) GetLogGroupsWithContext(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { input := &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, } @@ -33,13 +33,13 @@ func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGr } if !req.IsTargetingAllAccounts() { // TODO: accept more than one account id in search - input.AccountIdentifiers = []string{*req.AccountId} + input.AccountIdentifiers = []*string{req.AccountId} } } result := []resources.ResourceResponse[resources.LogGroup]{} for { - response, err := s.logGroupsAPI.DescribeLogGroups(ctx, input) + response, err := s.logGroupsAPI.DescribeLogGroupsWithContext(ctx, input) if err != nil || response == nil { return nil, err } @@ -63,7 +63,7 @@ func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGr return result, nil } -func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (s *LogGroupsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { input := &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupName: aws.String(request.LogGroupName), } @@ -73,7 +73,7 @@ func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resour // input.LogGroupName = nil // } - getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFields(ctx, input) + getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFieldsWithContext(ctx, input) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resour result = append(result, resources.ResourceResponse[resources.LogGroupField]{ Value: resources.LogGroupField{ Name: *logGroupField.Name, - Percent: int64(logGroupField.Percent), + Percent: *logGroupField.Percent, }, }) } diff --git a/pkg/tsdb/cloudwatch/services/log_groups_test.go b/pkg/tsdb/cloudwatch/services/log_groups_test.go index 992044bc1be..6b9a09170c3 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/services/log_groups_test.go @@ -5,14 +5,11 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -20,9 +17,9 @@ import ( func TestGetLogGroups(t *testing.T) { t.Run("Should map log groups response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return( + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return( &cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:333:log-group:group_c"), LogGroupName: utils.Pointer("group_c")}, @@ -30,7 +27,7 @@ func TestGetLogGroups(t *testing.T) { }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ @@ -51,10 +48,10 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should return an empty error if api doesn't return any data", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{}, resp) @@ -63,41 +60,41 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should only use LogGroupNamePrefix even if LogGroupNamePattern passed in resource call", func(t *testing.T) { // TODO: use LogGroupNamePattern when we have accounted for its behavior, still a little unexpected at the moment mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ Limit: 0, LogGroupNamePrefix: utils.Pointer("test"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("test"), }) }) t.Run("Should call api without LogGroupNamePrefix nor LogGroupNamePattern if not passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), }) }) t.Run("Should return an error when API returns error", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, fmt.Errorf("some error")) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.Error(t, err) assert.Equal(t, "some error", err.Error()) @@ -111,21 +108,21 @@ func TestGetLogGroups(t *testing.T) { ListAllLogGroups: false, } - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: aws.String("next_token"), }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), req) + resp, err := service.GetLogGroupsWithContext(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 1) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 1) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -143,30 +140,30 @@ func TestGetLogGroups(t *testing.T) { } // first call - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: utils.Pointer("token"), }, nil) // second call - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, NextToken: utils.Pointer("token"), }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), req) + resp, err := service.GetLogGroupsWithContext(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 2) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 2) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -183,36 +180,36 @@ func TestGetLogGroups(t *testing.T) { func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts or accountId if isCrossAccountEnabled is set to false", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should replace LogGroupNamePrefix if LogGroupNamePattern passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), LogGroupNamePattern: utils.Pointer("pattern"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("pattern"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -220,34 +217,34 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should includeLinkedAccounts,and accountId if isCrossAccountEnabled is set to true", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), IncludeLinkedAccounts: utils.Pointer(true), - AccountIdentifiers: []string{"accountId"}, + AccountIdentifiers: []*string{utils.Pointer("accountId")}, }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -255,26 +252,26 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts, or accountId if accountId is nil", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{ AccountId: utils.Pointer("accountId"), }, @@ -282,10 +279,10 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: aws.Int32(0), + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) @@ -294,24 +291,24 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { func TestGetLogGroupFields(t *testing.T) { t.Run("Should map log group fields response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { - Name: aws.String("field1"), - Percent: 10, + Name: utils.Pointer("field1"), + Percent: utils.Pointer(int64(10)), }, { - Name: aws.String("field2"), - Percent: 10, + Name: utils.Pointer("field2"), + Percent: utils.Pointer(int64(10)), }, { - Name: aws.String("field3"), - Percent: 10, + Name: utils.Pointer("field3"), + Percent: utils.Pointer(int64(10)), }, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{}) + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroupField]{ @@ -359,16 +356,16 @@ func TestGetLogGroupFields(t *testing.T) { // remove this test once the above test is uncommented t.Run("Should only set LogGroupName as api input in case both LogGroupName and LogGroupARN are specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "logGroupARN", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) @@ -378,16 +375,16 @@ func TestGetLogGroupFields(t *testing.T) { t.Run("Should only set LogGroupName as api input in case only LogGroupName is specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) diff --git a/pkg/tsdb/cloudwatch/services/regions.go b/pkg/tsdb/cloudwatch/services/regions.go index e0de066389b..2f8fc345e07 100644 --- a/pkg/tsdb/cloudwatch/services/regions.go +++ b/pkg/tsdb/cloudwatch/services/regions.go @@ -4,9 +4,7 @@ import ( "context" "sort" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -25,7 +23,7 @@ func NewRegionsService(ec2client models.EC2APIProvider, logger log.Logger) model } } -func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []ec2types.Region) { +func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []*ec2.Region) { for _, region := range ec2Regions { if _, ok := regions[*region.RegionName]; !ok { regions[*region.RegionName] = struct{}{} @@ -38,7 +36,7 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe result := make([]resources.ResourceResponse[resources.Region], 0) - ec2Regions, err := r.DescribeRegions(ctx, &ec2.DescribeRegionsInput{}) + ec2Regions, err := r.DescribeRegionsWithContext(ctx, &ec2.DescribeRegionsInput{}) // we ignore this error and always send default regions // we only fetch incase a user has enabled additional regions // but we still log it in case the user is expecting to fetch regions specific to their account and are unable to @@ -46,9 +44,7 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe r.Error("Failed to get regions: ", "error", err) } - if ec2Regions != nil { - mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) - } + mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) for region := range regions { result = append(result, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/services/regions_test.go b/pkg/tsdb/cloudwatch/services/regions_test.go index b352a493ba8..bb559ea5907 100644 --- a/pkg/tsdb/cloudwatch/services/regions_test.go +++ b/pkg/tsdb/cloudwatch/services/regions_test.go @@ -4,9 +4,7 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -19,14 +17,14 @@ var testLogger = log.New().With("logger", "test.logger") func TestRegions(t *testing.T) { t.Run("returns regions from the api and merges them with default regions", func(t *testing.T) { mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []ec2types.Region{ + Regions: []*ec2.Region{ { RegionName: utils.Pointer("earth-1"), }, }, } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegions").Return(mockRegions, nil) + ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, nil) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ @@ -44,9 +42,9 @@ func TestRegions(t *testing.T) { t.Run("always returns default regions, even if fetch fails", func(t *testing.T) { ec2Mock := &mocks.EC2Mock{} mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []ec2types.Region{}, + Regions: []*ec2.Region{}, } - ec2Mock.On("DescribeRegions").Return(mockRegions, assert.AnError) + ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, assert.AnError) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/sort_frame_test.go b/pkg/tsdb/cloudwatch/sort_frame_test.go index 2f136431d3e..cc7408d9c67 100644 --- a/pkg/tsdb/cloudwatch/sort_frame_test.go +++ b/pkg/tsdb/cloudwatch/sort_frame_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/aws" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" diff --git a/pkg/tsdb/cloudwatch/test_utils.go b/pkg/tsdb/cloudwatch/test_utils.go index e4ac75e7840..5d56ee96222 100644 --- a/pkg/tsdb/cloudwatch/test_utils.go +++ b/pkg/tsdb/cloudwatch/test_utils.go @@ -4,18 +4,17 @@ import ( "context" "strings" - "github.com/aws/smithy-go" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - - "github.com/grafana/grafana-aws-sdk/pkg/awsauth" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -27,6 +26,8 @@ import ( ) type fakeCWLogsClient struct { + cloudwatchlogsiface.CloudWatchLogsAPI + calls logsQueryCalls logGroups []cloudwatchlogs.DescribeLogGroupsOutput @@ -37,104 +38,83 @@ type fakeCWLogsClient struct { } type logsQueryCalls struct { - startQuery []*cloudwatchlogs.StartQueryInput - getEvents []*cloudwatchlogs.GetLogEventsInput - describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput + startQueryWithContext []*cloudwatchlogs.StartQueryInput + getEventsWithContext []*cloudwatchlogs.GetLogEventsInput + describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput } -func (m *fakeCWLogsClient) GetQueryResults(_ context.Context, _ *cloudwatchlogs.GetQueryResultsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { +func (m *fakeCWLogsClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { return &m.queryResults, nil } -func (m *fakeCWLogsClient) StartQuery(_ context.Context, input *cloudwatchlogs.StartQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - m.calls.startQuery = append(m.calls.startQuery, input) +func (m *fakeCWLogsClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { + m.calls.startQueryWithContext = append(m.calls.startQueryWithContext, input) return &cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil } -func (m *fakeCWLogsClient) StopQuery(_ context.Context, _ *cloudwatchlogs.StopQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { +func (m *fakeCWLogsClient) StopQueryWithContext(ctx context.Context, input *cloudwatchlogs.StopQueryInput, option ...request.Option) (*cloudwatchlogs.StopQueryOutput, error) { return &cloudwatchlogs.StopQueryOutput{ - Success: true, + Success: aws.Bool(true), }, nil } type mockLogsSyncClient struct { - mock.Mock -} - -func (m *mockLogsSyncClient) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { - return nil, nil -} + cloudwatchlogsiface.CloudWatchLogsAPI -func (m *mockLogsSyncClient) GetLogEvents(context.Context, *cloudwatchlogs.GetLogEventsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - return nil, nil -} - -func (m *mockLogsSyncClient) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - return nil, nil + mock.Mock } -func (m *mockLogsSyncClient) GetQueryResults(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *mockLogsSyncClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.GetQueryResultsOutput), args.Error(1) } -func (m *mockLogsSyncClient) StartQuery(ctx context.Context, input *cloudwatchlogs.StartQueryInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *mockLogsSyncClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.StartQueryOutput), args.Error(1) } -func (m *fakeCWLogsClient) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (m *fakeCWLogsClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { m.calls.describeLogGroups = append(m.calls.describeLogGroups, input) output := &m.logGroups[m.logGroupsIndex] m.logGroupsIndex++ return output, nil } -func (m *fakeCWLogsClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (m *fakeCWLogsClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return &m.logGroupFields, nil } -func (m *fakeCWLogsClient) GetLogEvents(_ context.Context, input *cloudwatchlogs.GetLogEventsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - m.calls.getEvents = append(m.calls.getEvents, input) +func (m *fakeCWLogsClient) GetLogEventsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { + m.calls.getEventsWithContext = append(m.calls.getEventsWithContext, input) return &cloudwatchlogs.GetLogEventsOutput{ - Events: []cloudwatchlogstypes.OutputLogEvent{}, + Events: []*cloudwatchlogs.OutputLogEvent{}, }, nil } type fakeCWAnnotationsClient struct { + cloudwatchiface.CloudWatchAPI calls annontationsQueryCalls describeAlarmsForMetricOutput *cloudwatch.DescribeAlarmsForMetricOutput describeAlarmsOutput *cloudwatch.DescribeAlarmsOutput } -func (c *fakeCWAnnotationsClient) DescribeAlarmHistory(ctx context.Context, input *cloudwatch.DescribeAlarmHistoryInput, f ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmHistoryOutput, error) { - return nil, nil -} - -func (c *fakeCWAnnotationsClient) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, f ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { - return nil, nil -} - -func (c *fakeCWAnnotationsClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, f ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return nil, nil -} - type annontationsQueryCalls struct { describeAlarmsForMetric []*cloudwatch.DescribeAlarmsForMetricInput describeAlarms []*cloudwatch.DescribeAlarmsInput } -func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(_ context.Context, params *cloudwatch.DescribeAlarmsForMetricInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(params *cloudwatch.DescribeAlarmsForMetricInput) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { c.calls.describeAlarmsForMetric = append(c.calls.describeAlarmsForMetric, params) return c.describeAlarmsForMetricOutput, nil } -func (c *fakeCWAnnotationsClient) DescribeAlarms(_ context.Context, params *cloudwatch.DescribeAlarmsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarms(params *cloudwatch.DescribeAlarmsInput) (*cloudwatch.DescribeAlarmsOutput, error) { c.calls.describeAlarms = append(c.calls.describeAlarms, params) return c.describeAlarmsOutput, nil @@ -142,14 +122,16 @@ func (c *fakeCWAnnotationsClient) DescribeAlarms(_ context.Context, params *clou // Please use mockEC2Client above, we are slowly migrating towards using testify's mocks only type oldEC2Client struct { + ec2iface.EC2API + regions []string - reservations []ec2types.Reservation + reservations []*ec2.Reservation } -func (c oldEC2Client) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { - regions := []ec2types.Region{} +func (c oldEC2Client) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, option ...request.Option) (*ec2.DescribeRegionsOutput, error) { + regions := []*ec2.Region{} for _, region := range c.regions { - regions = append(regions, ec2types.Region{ + regions = append(regions, &ec2.Region{ RegionName: aws.String(region), }) } @@ -158,10 +140,11 @@ func (c oldEC2Client) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsI }, nil } -func (c oldEC2Client) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { - reservations := []ec2types.Reservation{} +func (c oldEC2Client) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, + fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { + reservations := []*ec2.Reservation{} for _, r := range c.reservations { - instances := []ec2types.Instance{} + instances := []*ec2.Instance{} for _, inst := range r.Instances { if len(in.InstanceIds) == 0 { instances = append(instances, inst) @@ -169,85 +152,97 @@ func (c oldEC2Client) DescribeInstances(_ context.Context, in *ec2.DescribeInsta } for _, id := range in.InstanceIds { - if *inst.InstanceId == id { + if *inst.InstanceId == *id { instances = append(instances, inst) } } } - reservation := ec2types.Reservation{Instances: instances} + reservation := &ec2.Reservation{Instances: instances} reservations = append(reservations, reservation) } - return &ec2.DescribeInstancesOutput{ + fn(&ec2.DescribeInstancesOutput{ Reservations: reservations, - }, nil + }, true) + return nil } type fakeRGTAClient struct { - tagMapping []resourcegroupstaggingapitypes.ResourceTagMapping + resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI + + tagMapping []*resourcegroupstaggingapi.ResourceTagMapping } -func (c fakeRGTAClient) GetResources(_ context.Context, _ *resourcegroupstaggingapi.GetResourcesInput, _ ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) { - return &resourcegroupstaggingapi.GetResourcesOutput{ +func (c fakeRGTAClient) GetResourcesPagesWithContext(ctx context.Context, in *resourcegroupstaggingapi.GetResourcesInput, + fn func(*resourcegroupstaggingapi.GetResourcesOutput, bool) bool, opts ...request.Option) error { + fn(&resourcegroupstaggingapi.GetResourcesOutput{ ResourceTagMappingList: c.tagMapping, - }, nil + }, true) + return nil } type fakeCheckHealthClient struct { - listMetricsFunction func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) - describeLogGroupsFunction func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) - - models.CWClient + listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error + describeLogGroups func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) } -func (c fakeCheckHealthClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - if c.listMetricsFunction != nil { - return c.listMetricsFunction(ctx, input) +func (c fakeCheckHealthClient) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { + if c.listMetricsPages != nil { + return c.listMetricsPages(input, fn) } - return &cloudwatch.ListMetricsOutput{}, nil + return nil } -func (c fakeCheckHealthClient) DescribeLogGroups(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - if c.describeLogGroupsFunction != nil { - return c.describeLogGroupsFunction(ctx, input) +func (c fakeCheckHealthClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + if c.describeLogGroups != nil { + return c.describeLogGroups(input) } return nil, nil } -func (c fakeCheckHealthClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (c fakeCheckHealthClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return nil, nil } -func testInstanceManagerWithSettings(settings models.CloudWatchSettings, awsAuthShouldFail bool) instancemgmt.InstanceManager { - return datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{ - Settings: settings, - AWSConfigProvider: awsauth.NewFakeConfigProvider(awsAuthShouldFail), - tagValueCache: cache.New(0, 0), - }, nil - }) -} - -func testInstanceManager(pageLimit int, getAWSConfigShouldFail bool) instancemgmt.InstanceManager { - return testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-1", - AuthType: awsds.AuthTypeKeys, - AccessKey: "nothing", - SecretKey: "nowhere", +func testInstanceManager(pageLimit int) instancemgmt.InstanceManager { + return datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-1", + }, + GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, }, - GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, - }, getAWSConfigShouldFail) + sessions: &fakeSessionCache{}, + tagValueCache: cache.New(0, 0)}, nil + })) } func defaultTestInstanceManager() instancemgmt.InstanceManager { - return testInstanceManager(1000, false) + return testInstanceManager(1000) } -type FakeCredentialsProvider struct { +type mockSessionCache struct { + mock.Mock +} + +func (c *mockSessionCache) GetSessionWithAuthSettings(config awsds.GetSessionConfig, auth awsds.AuthSettings) (*session.Session, error) { + args := c.Called(config) + return args.Get(0).(*session.Session), args.Error(1) } -func (fcp *FakeCredentialsProvider) Retrieve(_ context.Context) (aws.Credentials, error) { - return aws.Credentials{}, nil +type fakeSessionCache struct { + getSessionWithAuthSettings func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) + calledRegions []string +} + +func (s *fakeSessionCache) GetSessionWithAuthSettings(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + s.calledRegions = append(s.calledRegions, c.Settings.Region) + + if s.getSessionWithAuthSettings != nil { + return s.getSessionWithAuthSettings(c, a) + } + return &session.Session{ + Config: &aws.Config{}, + }, nil } type mockedCallResourceResponseSenderForOauth struct { @@ -259,25 +254,25 @@ func (s *mockedCallResourceResponseSenderForOauth) Send(resp *backend.CallResour return nil } -type fakeSmithyError struct { +type fakeAWSError struct { code string message string } -func (f fakeSmithyError) Error() string { - return f.message +func (e fakeAWSError) OrigErr() error { + return nil } -func (f fakeSmithyError) ErrorCode() string { - return f.code +func (e fakeAWSError) Error() string { + return e.message } -func (f fakeSmithyError) ErrorMessage() string { - return f.message +func (e fakeAWSError) Code() string { + return e.code } -func (f fakeSmithyError) ErrorFault() smithy.ErrorFault { - return 0 +func (e fakeAWSError) Message() string { + return e.message } func contextWithFeaturesEnabled(enabled ...string) context.Context { diff --git a/pkg/tsdb/cloudwatch/time_series_query_test.go b/pkg/tsdb/cloudwatch/time_series_query_test.go index e51f0c58551..c2e65bb5434 100644 --- a/pkg/tsdb/cloudwatch/time_series_query_test.go +++ b/pkg/tsdb/cloudwatch/time_series_query_test.go @@ -6,12 +6,16 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" + "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -19,7 +23,6 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -33,19 +36,19 @@ func TestTimeSeriesQuery(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } t.Run("Custom metrics", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{ + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ + MetricDataResults: []*cloudwatch.MetricDataResult{ { - StatusCode: "Complete", Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []float64{1.0}, Timestamps: []time.Time{now}, + StatusCode: aws.String("Complete"), Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, }, { - StatusCode: "Complete", Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []float64{1.0}, Timestamps: []time.Time{now}, + StatusCode: aws.String("Complete"), Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, }}}, nil) im := defaultTestInstanceManager() @@ -142,23 +145,28 @@ func TestTimeSeriesQuery(t *testing.T) { func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMetricData_is_called_once_per_grouping_of_queries_by_region(t *testing.T) { /* TODO: This test aims to verify the logic to group regions which has been extracted from ParseMetricDataQueries. It should be replaced by a test at a lower level when grouping by regions is incorporated into a separate business logic layer */ - // FIXME: this test is broken - it only works because we're recovering from the panic that the Mock - // produces - see time_series_query.go line 78. If that recover is commented out, the test fails. - t.Skip("skipping broken test") origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient }) var mockMetricClient mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &mockMetricClient } - t.Run("Queries with the same region should call GetMetricData 1 time", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("Queries with the same region should call GetSessionWithAuthSettings with that region 1 time and call GetMetricDataWithContext 1 time", func(t *testing.T) { + mockSessionCache := &mockSessionCache{} + mockSessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-1" + })). // region from queries is asserted here + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: mockSessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -194,16 +202,31 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) + mockSessionCache.AssertExpectations(t) // method is defined to only return "Once()", // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 1) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 1) // GetMetricData is asserted to have been called 1 time for the 1 region present in the queries }) - t.Run("3 queries with 2 regions calls GetMetricData 2 times", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("3 queries with 2 regions calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { + sessionCache := &mockSessionCache{} + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-1" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-2" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -251,16 +274,26 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) + sessionCache.AssertExpectations(t) // method is defined to only return "Once()" for each region. // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each group of regions (2 regions total) }) - t.Run("3 queries with 2 time ranges calls GetMetricData 2 times", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("3 queries with 2 time ranges calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { + sessionCache := &mockSessionCache{} + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-2" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Times(2) + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -308,7 +341,8 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) + sessionCache.AssertExpectations(t) // method is defined to return twice (once for each batch) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each time range (2 time ranges total) }) } @@ -374,7 +408,7 @@ func newTestQuery(t testing.TB, p queryParameters) json.RawMessage { return marshalled } -func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { +func Test_QueryData_timeSeriesQuery_GetMetricDataWithContext(t *testing.T) { origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient @@ -382,15 +416,17 @@ func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) t.Run("passes query label as GetMetricData label", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) query := newTestQuery(t, queryParameters{ Label: aws.String("${PROP('Period')} some words ${PROP('Dim.InstanceId')}"), @@ -429,7 +465,7 @@ func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -463,21 +499,21 @@ func Test_QueryData_response_data_frame_name_is_always_response_label(t *testing NewCWClient = origNewCWClient }) - api := mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String(""), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, + api := mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String(""), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, }} - api.On("ListMetricsPages").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } labelFromGetMetricData := "some label" - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything). + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything). Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{ - {StatusCode: "Complete", Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), - Values: []float64{1.0}, Timestamps: []time.Time{{}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{ + {StatusCode: aws.String("Complete"), Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), + Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{{}}}, }}, nil) im := defaultTestInstanceManager() @@ -630,14 +666,14 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } im := defaultTestInstanceManager() t.Run("should call GetMetricDataInput with AccountId nil when no AccountId is provided", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -678,7 +714,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId nil when feature flag is false", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -719,7 +755,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId in a MetricStat query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -760,7 +796,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should GetMetricDataInput with AccountId in an inferred search expression query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ From f4849eabc705a2809b2e00a33c93524182aef2ae Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Mon, 24 Mar 2025 16:04:43 -0400 Subject: [PATCH 46/86] SQL Expressions: Change metric conversion to full long (#102728) When querying metric data (non-table data) with SQL Expressions, we need to convert the data to table format. This is alternative format which does not have the same issues with sparse data. There is now a __metric_name__ column and one __value__ column. Also a __display_name__ column if there is DisplayNameFromDS metadata. --------- Co-authored-by: Adam Simpson --- go.mod | 2 +- go.sum | 4 +- pkg/build/go.mod | 2 +- pkg/build/go.sum | 4 +- pkg/expr/convert_from_full_long.go | 128 ++++++ pkg/expr/convert_from_full_long_test.go | 192 ++++++++ pkg/expr/convert_to_full_long.go | 400 +++++++++++++++++ pkg/expr/convert_to_full_long_num_test.go | 507 ++++++++++++++++++++++ pkg/expr/convert_to_full_long_ts_test.go | 373 ++++++++++++++++ pkg/expr/convert_to_long.go | 311 ------------- pkg/expr/convert_to_long_test.go | 48 -- pkg/expr/nodes.go | 2 +- 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 +- 16 files changed, 1613 insertions(+), 372 deletions(-) create mode 100644 pkg/expr/convert_from_full_long.go create mode 100644 pkg/expr/convert_from_full_long_test.go create mode 100644 pkg/expr/convert_to_full_long.go create mode 100644 pkg/expr/convert_to_full_long_num_test.go create mode 100644 pkg/expr/convert_to_full_long_ts_test.go delete mode 100644 pkg/expr/convert_to_long.go delete mode 100644 pkg/expr/convert_to_long_test.go diff --git a/go.mod b/go.mod index d0e62735554..0098c0eb73b 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // @grafana/plugins-platform-backend github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad - github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad diff --git a/go.sum b/go.sum index 96df6362cc9..b5f34629318 100644 --- a/go.sum +++ b/go.sum @@ -839,8 +839,8 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 14352adba3e..ed6f08c804c 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -10,7 +10,7 @@ replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible require ( cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad - github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources github.com/docker/docker v27.5.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 1d55ac2a88b..55b8c06217d 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -53,8 +53,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= diff --git a/pkg/expr/convert_from_full_long.go b/pkg/expr/convert_from_full_long.go new file mode 100644 index 00000000000..c4de09cbee3 --- /dev/null +++ b/pkg/expr/convert_from_full_long.go @@ -0,0 +1,128 @@ +package expr + +import ( + "fmt" + + "github.com/grafana/grafana-plugin-sdk-go/data" +) + +func ConvertFromFullLongToNumericMulti(frames data.Frames) (data.Frames, error) { + if len(frames) != 1 { + return nil, fmt.Errorf("expected exactly one frame, got %d", len(frames)) + } + frame := frames[0] + if frame.Meta == nil || frame.Meta.Type != numericFullLongType { + return nil, fmt.Errorf("expected frame of type %q", numericFullLongType) + } + + var ( + metricField *data.Field + valueField *data.Field + displayField *data.Field + labelFields []*data.Field + ) + + // Identify key fields + for _, f := range frame.Fields { + switch f.Name { + case SQLMetricFieldName: + metricField = f + case SQLValueFieldName: + valueField = f + case SQLDisplayFieldName: + displayField = f + default: + if f.Type() == data.FieldTypeNullableString { + labelFields = append(labelFields, f) + } + } + } + + if metricField == nil || valueField == nil { + return nil, fmt.Errorf("missing required fields: %q or %q", SQLMetricFieldName, SQLValueFieldName) + } + + type seriesKey struct { + metric string + labelFP data.Fingerprint + displayName string + } + + type seriesEntry struct { + indices []int + labels data.Labels + displayName *string + } + + grouped := make(map[seriesKey]*seriesEntry) + + for i := 0; i < frame.Rows(); i++ { + if valueField.NilAt(i) { + continue // skip null values + } + + metric := metricField.At(i).(string) + + // collect labels + labels := data.Labels{} + for _, f := range labelFields { + if f.NilAt(i) { + continue + } + val := f.At(i).(*string) + if val != nil { + labels[f.Name] = *val + } + } + fp := labels.Fingerprint() + + // handle optional display name + var displayPtr *string + displayKey := "" + if displayField != nil && !displayField.NilAt(i) { + if raw := displayField.At(i).(*string); raw != nil { + displayPtr = raw + displayKey = *raw + } + } + + key := seriesKey{ + metric: metric, + labelFP: fp, + displayName: displayKey, + } + + entry, ok := grouped[key] + if !ok { + entry = &seriesEntry{ + labels: labels, + displayName: displayPtr, + } + grouped[key] = entry + } + entry.indices = append(entry.indices, i) + } + + var result data.Frames + for key, entry := range grouped { + values := make([]*float64, 0, len(entry.indices)) + for _, i := range entry.indices { + v, err := valueField.FloatAt(i) + if err != nil { + return nil, fmt.Errorf("failed to convert value at index %d to float: %w", i, err) + } + values = append(values, &v) + } + + field := data.NewField(key.metric, entry.labels, values) + if entry.displayName != nil { + field.Config = &data.FieldConfig{DisplayNameFromDS: *entry.displayName} + } + + frame := data.NewFrame("", field) + frame.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + result = append(result, frame) + } + + return result, nil +} diff --git a/pkg/expr/convert_from_full_long_test.go b/pkg/expr/convert_from_full_long_test.go new file mode 100644 index 00000000000..2487fac661d --- /dev/null +++ b/pkg/expr/convert_from_full_long_test.go @@ -0,0 +1,192 @@ +package expr + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/require" +) + +func TestConvertFromFullLongToNumericMulti(t *testing.T) { + t.Run("SingleRowNoLabels", func(t *testing.T) { + input := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(3.14)}), + ) + input.Meta = &data.FrameMeta{Type: numericFullLongType} + + out, err := ConvertFromFullLongToNumericMulti(data.Frames{input}) + require.NoError(t, err) + require.Len(t, out, 1) + + expected := data.NewFrame("", + data.NewField("cpu", nil, []*float64{fp(3.14)}), + ) + expected.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + if diff := cmp.Diff(expected, out[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoRowsWithLabelsAndDisplay", func(t *testing.T) { + input := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField(SQLDisplayFieldName, nil, []*string{sp("CPU A"), sp("CPU A")}), + data.NewField("host", nil, []*string{sp("a"), sp("a")}), + ) + input.Meta = &data.FrameMeta{Type: numericFullLongType} + + out, err := ConvertFromFullLongToNumericMulti(data.Frames{input}) + require.NoError(t, err) + require.Len(t, out, 1) + + expected := data.NewFrame("", + func() *data.Field { + f := data.NewField("cpu", data.Labels{"host": "a"}, []*float64{fp(1.0), fp(2.0)}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU A"} + return f + }(), + ) + expected.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + if diff := cmp.Diff(expected, out[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("SkipsNullValues", func(t *testing.T) { + input := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), nil}), + ) + input.Meta = &data.FrameMeta{Type: numericFullLongType} + + out, err := ConvertFromFullLongToNumericMulti(data.Frames{input}) + require.NoError(t, err) + require.Len(t, out, 1) + + expected := data.NewFrame("", + data.NewField("cpu", nil, []*float64{fp(1.0)}), + ) + expected.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + if diff := cmp.Diff(expected, out[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) +} + +func TestConvertNumericMultiRoundTripToFullLongAndBack(t *testing.T) { + t.Run("TwoFieldsWithSparseLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []*float64{fp(1.0)}), + ), + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []*float64{fp(2.0)}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + fullLong, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, fullLong, 1) + + roundTrip, err := ConvertFromFullLongToNumericMulti(fullLong) + require.NoError(t, err) + + expected := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []*float64{fp(1.0)}), + ), + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []*float64{fp(2.0)}), + ), + } + for _, f := range expected { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + sortFramesByMetricDisplayAndLabels(expected) + sortFramesByMetricDisplayAndLabels(roundTrip) + + require.Len(t, roundTrip, len(expected)) + for i := range expected { + if diff := cmp.Diff(expected[i], roundTrip[i], data.FrameTestCompareOptions()...); diff != "" { + t.Errorf("Mismatch on frame %d (-want +got):\n%s", i, diff) + } + } + }) + + t.Run("PreservesDisplayName", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + func() *data.Field { + f := data.NewField("cpu", data.Labels{"host": "a"}, []*float64{fp(1.0)}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU A"} + return f + }(), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + fullLong, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, fullLong, 1) + + roundTrip, err := ConvertFromFullLongToNumericMulti(fullLong) + require.NoError(t, err) + + expected := data.Frames{ + data.NewFrame("", + func() *data.Field { + f := data.NewField("cpu", data.Labels{"host": "a"}, []*float64{fp(1.0)}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU A"} + return f + }(), + ), + } + expected[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + sortFramesByMetricDisplayAndLabels(expected) + sortFramesByMetricDisplayAndLabels(roundTrip) + + require.Len(t, roundTrip, 1) + if diff := cmp.Diff(expected[0], roundTrip[0], data.FrameTestCompareOptions()...); diff != "" { + t.Errorf("Mismatch (-want +got):\n%s", diff) + } + }) +} + +func sortFramesByMetricDisplayAndLabels(frames data.Frames) { + sort.Slice(frames, func(i, j int) bool { + fi := frames[i].Fields[0] + fj := frames[j].Fields[0] + + // 1. Metric name + if fi.Name != fj.Name { + return fi.Name < fj.Name + } + + // 2. Display name (if set) + var di, dj string + if fi.Config != nil { + di = fi.Config.DisplayNameFromDS + } + if fj.Config != nil { + dj = fj.Config.DisplayNameFromDS + } + if di != dj { + return di < dj + } + + // 3. Labels fingerprint + return fi.Labels.Fingerprint() < fj.Labels.Fingerprint() + }) +} diff --git a/pkg/expr/convert_to_full_long.go b/pkg/expr/convert_to_full_long.go new file mode 100644 index 00000000000..94dc2648bd6 --- /dev/null +++ b/pkg/expr/convert_to_full_long.go @@ -0,0 +1,400 @@ +package expr + +import ( + "fmt" + "sort" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/data" +) + +const ( + SQLMetricFieldName = "__metric_name__" + SQLValueFieldName = "__value__" + SQLDisplayFieldName = "__display_name__" + + // These are not types in the SDK or dataplane contract yet. + numericFullLongType = "numeric_full_long" + timeseriesFullLongType = "time_series_full_long" +) + +func ConvertToFullLong(frames data.Frames) (data.Frames, error) { + if len(frames) == 0 { + return frames, nil + } + + var inputType data.FrameType + if frames[0].Meta != nil && frames[0].Meta.Type != "" { + inputType = frames[0].Meta.Type + } else { + return nil, fmt.Errorf("input frame missing FrameMeta.Type") + } + + if !supportedToLongConversion(inputType) { + return nil, fmt.Errorf("unsupported input dataframe type %s for full long conversion", inputType) + } + + switch inputType { + case data.FrameTypeNumericMulti: + return convertNumericMultiToFullLong(frames) + case data.FrameTypeNumericWide: + return convertNumericWideToFullLong(frames) + case data.FrameTypeTimeSeriesMulti: + return convertTimeSeriesMultiToFullLong(frames) + case data.FrameTypeTimeSeriesWide: + return convertTimeSeriesWideToFullLong(frames) + default: + return nil, fmt.Errorf("unsupported input type %s for full long conversion", inputType) + } +} + +func convertNumericMultiToFullLong(frames data.Frames) (data.Frames, error) { + wide := convertNumericMultiToNumericWide(frames) + return convertNumericWideToFullLong(wide) +} + +func convertNumericWideToFullLong(frames data.Frames) (data.Frames, error) { + if len(frames) != 1 { + return nil, fmt.Errorf("expected exactly one frame for wide format, but got %d", len(frames)) + } + inputFrame := frames[0] + if inputFrame.Rows() > 1 { + return nil, fmt.Errorf("expected no more than one row in the frame, but got %d", inputFrame.Rows()) + } + + var ( + metricCol = make([]string, 0, len(inputFrame.Fields)) + valueCol = make([]*float64, 0, len(inputFrame.Fields)) + displayCol = make([]*string, 0, len(inputFrame.Fields)) + hasDisplayCol bool + ) + + labelKeySet := map[string]struct{}{} + for _, field := range inputFrame.Fields { + if !field.Type().Numeric() { + continue + } + val, err := field.FloatAt(0) + if err != nil { + continue + } + v := val + valueCol = append(valueCol, &v) + metricCol = append(metricCol, field.Name) + + // Display name + var d *string + if field.Config != nil && field.Config.DisplayNameFromDS != "" { + s := field.Config.DisplayNameFromDS + d = &s + hasDisplayCol = true + } + displayCol = append(displayCol, d) + + for k := range field.Labels { + labelKeySet[k] = struct{}{} + } + } + + labelKeys := make([]string, 0, len(labelKeySet)) + + labelValues := make(map[string][]*string) + for k := range labelKeySet { + labelKeys = append(labelKeys, k) + labelValues[k] = make([]*string, 0, len(valueCol)) + } + sort.Strings(labelKeys) + + for _, field := range inputFrame.Fields { + if !field.Type().Numeric() { + continue + } + for _, k := range labelKeys { + var val *string + if field.Labels != nil { + if v, ok := field.Labels[k]; ok { + val = &v + } + } + labelValues[k] = append(labelValues[k], val) + } + } + + fields := []*data.Field{ + data.NewField(SQLMetricFieldName, nil, metricCol), + data.NewField(SQLValueFieldName, nil, valueCol), + } + if hasDisplayCol { + fields = append(fields, data.NewField(SQLDisplayFieldName, nil, displayCol)) + } + for _, k := range labelKeys { + fields = append(fields, data.NewField(k, nil, labelValues[k])) + } + + out := data.NewFrame("", fields...) + out.Meta = &data.FrameMeta{Type: numericFullLongType} + return data.Frames{out}, nil +} + +func convertTimeSeriesMultiToFullLong(frames data.Frames) (data.Frames, error) { + type row struct { + t time.Time + value *float64 + metric string + display *string + labels data.Labels + } + + var rows []row + labelKeysSet := map[string]struct{}{} + hasDisplayCol := false + + for _, frame := range frames { + var timeField *data.Field + for _, f := range frame.Fields { + if f.Type() == data.FieldTypeTime { + timeField = f + break + } + } + if timeField == nil { + return nil, fmt.Errorf("missing time field") + } + for _, f := range frame.Fields { + if !f.Type().Numeric() { + continue + } + var display *string + if f.Config != nil && f.Config.DisplayNameFromDS != "" { + s := f.Config.DisplayNameFromDS + display = &s + hasDisplayCol = true + } + for i := 0; i < f.Len(); i++ { + t := timeField.At(i).(time.Time) + v, err := f.FloatAt(i) + if err != nil { + continue + } + val := v + rows = append(rows, row{ + t: t, + value: &val, + metric: f.Name, + display: display, + labels: f.Labels, + }) + for k := range f.Labels { + labelKeysSet[k] = struct{}{} + } + } + } + } + + labelKeys := make([]string, 0, len(labelKeysSet)) + for k := range labelKeysSet { + labelKeys = append(labelKeys, k) + } + sort.Strings(labelKeys) + sort.SliceStable(rows, func(i, j int) bool { + if rows[i].t.Equal(rows[j].t) { + return rows[i].metric < rows[j].metric + } + return rows[i].t.Before(rows[j].t) + }) + + times := make([]time.Time, len(rows)) + values := make([]*float64, len(rows)) + metrics := make([]string, len(rows)) + var displays []*string + if hasDisplayCol { + displays = make([]*string, len(rows)) + } + labels := make(map[string][]*string) + for _, k := range labelKeys { + labels[k] = make([]*string, len(rows)) + } + + for i, r := range rows { + times[i] = r.t + values[i] = r.value + metrics[i] = r.metric + if hasDisplayCol { + displays[i] = r.display + } + for _, k := range labelKeys { + if v, ok := r.labels[k]; ok { + labels[k][i] = &v + } + } + } + + fields := []*data.Field{ + data.NewField("time", nil, times), + data.NewField(SQLValueFieldName, nil, values), + data.NewField(SQLMetricFieldName, nil, metrics), + } + if hasDisplayCol { + fields = append(fields, data.NewField(SQLDisplayFieldName, nil, displays)) + } + for _, k := range labelKeys { + fields = append(fields, data.NewField(k, nil, labels[k])) + } + + out := data.NewFrame("", fields...) + out.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + return data.Frames{out}, nil +} + +func convertTimeSeriesWideToFullLong(frames data.Frames) (data.Frames, error) { + if len(frames) != 1 { + return nil, fmt.Errorf("expected exactly one frame for wide format, but got %d", len(frames)) + } + frame := frames[0] + + var timeField *data.Field + for _, f := range frame.Fields { + if f.Type() == data.FieldTypeTime { + timeField = f + break + } + } + if timeField == nil { + return nil, fmt.Errorf("time field not found in TimeSeriesWide frame") + } + + type row struct { + t time.Time + value *float64 + metric string + display *string + labels data.Labels + } + + var ( + rows []row + labelKeysSet = map[string]struct{}{} + hasDisplayCol bool + ) + + // Collect all label keys + for _, f := range frame.Fields { + if !f.Type().Numeric() { + continue + } + for k := range f.Labels { + labelKeysSet[k] = struct{}{} + } + } + + labelKeys := make([]string, 0, len(labelKeysSet)) + for k := range labelKeysSet { + labelKeys = append(labelKeys, k) + } + sort.Strings(labelKeys) + + timeLen := timeField.Len() + for _, f := range frame.Fields { + if !f.Type().Numeric() { + continue + } + var display *string + if f.Config != nil && f.Config.DisplayNameFromDS != "" { + s := f.Config.DisplayNameFromDS + display = &s + hasDisplayCol = true + } + for i := 0; i < timeLen; i++ { + t := timeField.At(i).(time.Time) + v, err := f.FloatAt(i) + if err != nil { + continue + } + val := v + rows = append(rows, row{ + t: t, + value: &val, + metric: f.Name, + display: display, + labels: f.Labels, + }) + } + } + + sort.SliceStable(rows, func(i, j int) bool { + if rows[i].t.Equal(rows[j].t) { + return rows[i].metric < rows[j].metric + } + return rows[i].t.Before(rows[j].t) + }) + + times := make([]time.Time, len(rows)) + values := make([]*float64, len(rows)) + metrics := make([]string, len(rows)) + var displays []*string + if hasDisplayCol { + displays = make([]*string, len(rows)) + } + labels := make(map[string][]*string) + for _, k := range labelKeys { + labels[k] = make([]*string, len(rows)) + } + + for i, r := range rows { + times[i] = r.t + values[i] = r.value + metrics[i] = r.metric + if hasDisplayCol { + displays[i] = r.display + } + for _, k := range labelKeys { + if v, ok := r.labels[k]; ok { + labels[k][i] = &v + } + } + } + + fields := []*data.Field{ + data.NewField("time", nil, times), + data.NewField(SQLValueFieldName, nil, values), + data.NewField(SQLMetricFieldName, nil, metrics), + } + if hasDisplayCol { + fields = append(fields, data.NewField(SQLDisplayFieldName, nil, displays)) + } + for _, k := range labelKeys { + fields = append(fields, data.NewField(k, nil, labels[k])) + } + + out := data.NewFrame("", fields...) + out.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + return data.Frames{out}, nil +} + +func supportedToLongConversion(inputType data.FrameType) bool { + switch inputType { + case data.FrameTypeNumericMulti, data.FrameTypeNumericWide: + return true + case data.FrameTypeTimeSeriesMulti, data.FrameTypeTimeSeriesWide: + return true + default: + return false + } +} + +func convertNumericMultiToNumericWide(frames data.Frames) data.Frames { + if len(frames) == 0 { + return nil + } + + out := data.NewFrame("") + for _, frame := range frames { + for _, field := range frame.Fields { + if field.Type().Numeric() { + out.Fields = append(out.Fields, field) + } + } + } + out.Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + return data.Frames{out} +} diff --git a/pkg/expr/convert_to_full_long_num_test.go b/pkg/expr/convert_to_full_long_num_test.go new file mode 100644 index 00000000000..88a157f89aa --- /dev/null +++ b/pkg/expr/convert_to_full_long_num_test.go @@ -0,0 +1,507 @@ +package expr + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/require" +) + +func TestConvertNumericWideToFullLong(t *testing.T) { + t.Run("SingleItemNoLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", nil, []float64{3.14}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(3.14)}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("MultiRowShouldError", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", nil, []float64{1.0, 2.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + _, err := ConvertToFullLong(input) + require.Error(t, err) + require.Contains(t, err.Error(), "no more than one row") + }) + + t.Run("TwoItemsWithSingleLabel", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + data.NewField("cpu", data.Labels{"host": "b"}, []float64{2.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField("host", nil, []*string{sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoItemsWithSparseLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []float64{2.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField("env", nil, []*string{nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoDifferentMetricsWithSharedLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + data.NewField("mem", data.Labels{"host": "a"}, []float64{4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(4.0)}), + data.NewField("host", nil, []*string{sp("a"), sp("a")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSparseMetricsAndLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + data.NewField("mem", data.Labels{"env": "prod"}, []float64{4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(4.0)}), + data.NewField("env", nil, []*string{nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), nil}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("ExtraTimeFieldIsDropped", func(t *testing.T) { + // Note we may consider changing this behavior and looking into keeping + // remainder fields in the future. + input := data.Frames{ + data.NewFrame("numeric", + data.NewField("timestamp", nil, []time.Time{time.Now()}), // extra time field + data.NewField("cpu", nil, []float64{1.23}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.23)}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) +} + +func TestConvertNumericWideToFullLongWithDisplayName(t *testing.T) { + t.Run("SingleFieldWithDisplayName", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + func() *data.Field { + f := data.NewField("cpu", nil, []float64{3.14}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU Display"} + return f + }(), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(3.14)}), + data.NewField(SQLDisplayFieldName, nil, []*string{sp("CPU Display")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("MixedDisplayNames", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("numeric", + func() *data.Field { + f := data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU A"} + return f + }(), + data.NewField("cpu", data.Labels{"host": "b"}, []float64{2.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericWide} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField(SQLDisplayFieldName, nil, []*string{sp("CPU A"), nil}), + data.NewField("host", nil, []*string{sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) +} + +func TestConvertNumericMultiToFullLong(t *testing.T) { + t.Run("SingleItemNoLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", nil, []float64{3.14}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(3.14)}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoItemsWithSingleLabel", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + ), + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "b"}, []float64{2.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField("host", nil, []*string{sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoItemsWithSparseLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + ), + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []float64{2.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField("env", nil, []*string{nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoDifferentMetricsWithSharedLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + ), + data.NewFrame("", + data.NewField("mem", data.Labels{"host": "a"}, []float64{4.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(4.0)}), + data.NewField("host", nil, []*string{sp("a"), sp("a")}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSparseMetricsAndLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0}), + ), + data.NewFrame("", + data.NewField("mem", data.Labels{"env": "prod"}, []float64{4.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeNumericMulti} + } + + expected := data.NewFrame("", + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem"}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(4.0)}), + data.NewField("env", nil, []*string{nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), nil}), + ) + expected.Meta = &data.FrameMeta{Type: numericFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) +} + +func TestConvertTimeSeriesWideToFullLong(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + t.Run("SingleSeriesNoLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("time", nil, times), + data.NewField("cpu", nil, []float64{1.0, 2.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesWide} + + expected := data.NewFrame("", + data.NewField("time", nil, times), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoSeriesOneLabel", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + data.NewField("cpu", data.Labels{"host": "b"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesWide} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{times[0], times[0], times[1], times[1]}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu", "cpu"}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoMetricsWithSharedLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + data.NewField("mem", data.Labels{"host": "a"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesWide} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{times[0], times[0], times[1], times[1]}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem", "cpu", "mem"}), + data.NewField("host", nil, []*string{sp("a"), sp("a"), sp("a"), sp("a")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoSeriesSparseLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesWide} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{times[0], times[0], times[1], times[1]}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu", "cpu"}), + data.NewField("env", nil, []*string{nil, sp("prod"), nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoSeriesSparseMetricsAndLabels", func(t *testing.T) { + input := data.Frames{ + data.NewFrame("", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + data.NewField("mem", data.Labels{"host": "b", "env": "prod"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesWide} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{times[0], times[0], times[1], times[1]}), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem", "cpu", "mem"}), + data.NewField("env", nil, []*string{nil, sp("prod"), nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) +} + +func sp(s string) *string { + return &s +} diff --git a/pkg/expr/convert_to_full_long_ts_test.go b/pkg/expr/convert_to_full_long_ts_test.go new file mode 100644 index 00000000000..eb43d9324fe --- /dev/null +++ b/pkg/expr/convert_to_full_long_ts_test.go @@ -0,0 +1,373 @@ +package expr + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/require" +) + +func TestConvertTimeSeriesMultiToFullLong(t *testing.T) { + t.Run("SingleSeriesNoLabels", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + time.Unix(20, 0), + } + values := []float64{1.0, 2.0, 3.0} + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", nil, values), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, times), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0), fp(3.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu"}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSeriesOneLabel", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "b"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + input[1].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), time.Unix(0, 0), time.Unix(10, 0), time.Unix(10, 0), + }), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu", "cpu"}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoMetricsWithSharedLabels", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("mem", + data.NewField("time", nil, times), + data.NewField("mem", data.Labels{"host": "a"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + input[1].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), time.Unix(0, 0), time.Unix(10, 0), time.Unix(10, 0), + }), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem", "cpu", "mem"}), + data.NewField("host", nil, []*string{sp("a"), sp("a"), sp("a"), sp("a")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSeriesSparseLabels", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "b", "env": "prod"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + input[1].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), time.Unix(0, 0), time.Unix(10, 0), time.Unix(10, 0), + }), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu", "cpu"}), + data.NewField("env", nil, []*string{nil, sp("prod"), nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSeriesSparseMetrics", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("mem", + data.NewField("time", nil, times), + data.NewField("mem", data.Labels{"host": "b"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + input[1].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), time.Unix(0, 0), time.Unix(10, 0), time.Unix(10, 0), + }), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem", "cpu", "mem"}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("TwoSeriesSparseMetricsAndLabels", func(t *testing.T) { + times := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("mem", + data.NewField("time", nil, times), + data.NewField("mem", data.Labels{"host": "b", "env": "prod"}, []float64{3.0, 4.0}), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + input[1].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), time.Unix(0, 0), time.Unix(10, 0), time.Unix(10, 0), + }), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(3.0), fp(2.0), fp(4.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "mem", "cpu", "mem"}), + data.NewField("env", nil, []*string{nil, sp("prod"), nil, sp("prod")}), + data.NewField("host", nil, []*string{sp("a"), sp("b"), sp("a"), sp("b")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) + + t.Run("ThreeSeriesSparseTimeLabelsMetrics", func(t *testing.T) { + timesA := []time.Time{ + time.Unix(0, 0), + time.Unix(10, 0), + } + timesB := []time.Time{ + time.Unix(5, 0), + time.Unix(15, 0), + } + timesMem := []time.Time{ + time.Unix(10, 0), + time.Unix(30, 0), + } + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, timesA), + data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}), + ), + data.NewFrame("cpu", + data.NewField("time", nil, timesB), + data.NewField("cpu", nil, []float64{9.0, 10.0}), // no labels + ), + data.NewFrame("mem", + data.NewField("time", nil, timesMem), + data.NewField("mem", data.Labels{"host": "b", "env": "prod"}, []float64{3.0, 4.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + } + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + time.Unix(0, 0), // cpu a + time.Unix(5, 0), // cpu no label + time.Unix(10, 0), // cpu a + time.Unix(10, 0), // mem + time.Unix(15, 0), // cpu no label + time.Unix(30, 0), // mem + }), + data.NewField(SQLValueFieldName, nil, []*float64{ + fp(1.0), fp(9.0), fp(2.0), fp(3.0), fp(10.0), fp(4.0), + }), + data.NewField(SQLMetricFieldName, nil, []string{ + "cpu", "cpu", "cpu", "mem", "cpu", "mem", + }), + data.NewField("env", nil, []*string{ + nil, nil, nil, sp("prod"), nil, sp("prod"), + }), + data.NewField("host", nil, []*string{ + sp("a"), nil, sp("a"), sp("b"), nil, sp("b"), + }), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Result mismatch (-want +got):%s", diff) + } + }) +} + +func TestConvertTimeSeriesMultiToFullLongWithDisplayName(t *testing.T) { + t.Run("SingleSeriesWithDisplayName", func(t *testing.T) { + times := []time.Time{time.Unix(0, 0), time.Unix(10, 0)} + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + func() *data.Field { + f := data.NewField("cpu", nil, []float64{1.0, 2.0}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU Display"} + return f + }(), + ), + } + input[0].Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + + expected := data.NewFrame("", + data.NewField("time", nil, times), + data.NewField(SQLValueFieldName, nil, []*float64{fp(1.0), fp(2.0)}), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu"}), + data.NewField(SQLDisplayFieldName, nil, []*string{sp("CPU Display"), sp("CPU Display")}), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("TwoSeriesMixedDisplayNames", func(t *testing.T) { + times := []time.Time{time.Unix(0, 0), time.Unix(10, 0)} + + input := data.Frames{ + data.NewFrame("cpu", + data.NewField("time", nil, times), + func() *data.Field { + f := data.NewField("cpu", data.Labels{"host": "a"}, []float64{1.0, 2.0}) + f.Config = &data.FieldConfig{DisplayNameFromDS: "CPU A"} + return f + }(), + ), + data.NewFrame("cpu", + data.NewField("time", nil, times), + data.NewField("cpu", data.Labels{"host": "b"}, []float64{3.0, 4.0}), + ), + } + for _, f := range input { + f.Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti} + } + + expected := data.NewFrame("", + data.NewField("time", nil, []time.Time{ + times[0], times[0], times[1], times[1], + }), + data.NewField(SQLValueFieldName, nil, []*float64{ + fp(1.0), fp(3.0), fp(2.0), fp(4.0), + }), + data.NewField(SQLMetricFieldName, nil, []string{"cpu", "cpu", "cpu", "cpu"}), + data.NewField(SQLDisplayFieldName, nil, []*string{ + sp("CPU A"), nil, sp("CPU A"), nil, + }), + data.NewField("host", nil, []*string{ + sp("a"), sp("b"), sp("a"), sp("b"), + }), + ) + expected.Meta = &data.FrameMeta{Type: timeseriesFullLongType} + + output, err := ConvertToFullLong(input) + require.NoError(t, err) + require.Len(t, output, 1) + if diff := cmp.Diff(expected, output[0], data.FrameTestCompareOptions()...); diff != "" { + require.FailNowf(t, "Mismatch (-want +got):\n%s", diff) + } + }) +} diff --git a/pkg/expr/convert_to_long.go b/pkg/expr/convert_to_long.go deleted file mode 100644 index 3ab3339b8b0..00000000000 --- a/pkg/expr/convert_to_long.go +++ /dev/null @@ -1,311 +0,0 @@ -package expr - -import ( - "fmt" - "sort" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/data" -) - -func ConvertToLong(frames data.Frames) (data.Frames, error) { - if len(frames) == 0 { - // general empty case for now - return frames, nil - } - // Four Conversion Possible Cases - // 1. NumericMulti -> NumericLong - // 2. NumericWide -> NumericLong - // 3. TimeSeriesMulti -> TimeSeriesLong - // 4. TimeSeriesWide -> TimeSeriesLong - - // Detect if input type is declared - // First Check Frame Meta Type - - var inputType data.FrameType - if frames[0].Meta != nil && frames[0].Meta.Type != "" { - inputType = frames[0].Meta.Type - } - - // TODO: Add some guessing of Type if not declared - if inputType == "" { - return frames, fmt.Errorf("no input dataframe type set") - } - - if !supportedToLongConversion(inputType) { - return frames, fmt.Errorf("unsupported input dataframe type %s for SQL expression", inputType) - } - - toLong := getToLongConversionFunc(inputType) - if toLong == nil { - return frames, fmt.Errorf("could not get conversion function for input type %s", inputType) - } - - return toLong(frames) -} - -func convertNumericMultiToNumericLong(frames data.Frames) (data.Frames, error) { - // Apart from metadata, NumericMulti is basically NumericWide, except one frame per thing - // so we collapse into wide and call the wide conversion - wide := convertNumericMultiToNumericWide(frames) - return convertNumericWideToNumericLong(wide) -} - -func convertNumericMultiToNumericWide(frames data.Frames) data.Frames { - newFrame := data.NewFrame("") - for _, frame := range frames { - for _, field := range frame.Fields { - if !field.Type().Numeric() { - continue - } - newField := data.NewFieldFromFieldType(field.Type(), field.Len()) - newField.Name = field.Name - newField.Labels = field.Labels.Copy() - if field.Len() == 1 { - newField.Set(0, field.CopyAt(0)) - } - newFrame.Fields = append(newFrame.Fields, newField) - } - } - return data.Frames{newFrame} -} - -func convertNumericWideToNumericLong(frames data.Frames) (data.Frames, error) { - // Wide should only be one frame - if len(frames) != 1 { - return nil, fmt.Errorf("expected exactly one frame for wide format, but got %d", len(frames)) - } - inputFrame := frames[0] - - // The Frame should have no more than one row - if inputFrame.Rows() > 1 { - return nil, fmt.Errorf("expected no more than one row in the frame, but got %d", inputFrame.Rows()) - } - - // Gather: - // - unique numeric Field Names, and - // - unique Label Keys (from Numeric Fields only) - // each one maps to a field in the output long Frame. - uniqueNames := make([]string, 0) - uniqueKeys := make([]string, 0) - - uniqueNamesMap := make(map[string]data.FieldType) - uniqueKeysMap := make(map[string]struct{}) - - prints := make(map[string]int) - - registerPrint := func(labels data.Labels) { - fp := labels.Fingerprint().String() - if _, ok := prints[fp]; !ok { - prints[fp] = len(prints) - } - } - - for _, field := range inputFrame.Fields { - if field.Type().Numeric() { - if _, ok := uniqueNamesMap[field.Name]; !ok { - uniqueNames = append(uniqueNames, field.Name) - uniqueNamesMap[field.Name] = field.Type() - } - - if field.Labels != nil { - registerPrint(field.Labels) - for key := range field.Labels { - if _, ok := uniqueKeysMap[key]; !ok { - uniqueKeys = append(uniqueKeys, key) - } - uniqueKeysMap[key] = struct{}{} - } - } - } - } - - // Create new fields for output Long frame - fields := make([]*data.Field, 0, len(uniqueNames)+len(uniqueKeys)) - - // Create the Numeric Fields, tracking the index of each field by name - // Note: May want to use FloatAt and and prepopulate with NaN so missing - // combinations of value can be NA instead of the zero value of 0. - var nameIndexMap = make(map[string]int, len(uniqueNames)) - for i, name := range uniqueNames { - field := data.NewFieldFromFieldType(uniqueNamesMap[name], len(prints)) - field.Name = name - fields = append(fields, field) - nameIndexMap[name] = i - } - - // Create the String fields, tracking the index of each field by key - var keyIndexMap = make(map[string]int, len(uniqueKeys)) - for i, k := range uniqueKeys { - fields = append(fields, data.NewField(k, nil, make([]string, len(prints)))) - keyIndexMap[k] = len(nameIndexMap) + i - } - - longFrame := data.NewFrame("", fields...) - - if inputFrame.Rows() == 0 { - return data.Frames{longFrame}, nil - } - - // Add Rows to the fields - for _, field := range inputFrame.Fields { - if !field.Type().Numeric() { - continue - } - fieldIdx := prints[field.Labels.Fingerprint().String()] - longFrame.Fields[nameIndexMap[field.Name]].Set(fieldIdx, field.CopyAt(0)) - for key, value := range field.Labels { - longFrame.Fields[keyIndexMap[key]].Set(fieldIdx, value) - } - } - - return data.Frames{longFrame}, nil -} - -func convertTimeSeriesMultiToTimeSeriesLong(frames data.Frames) (data.Frames, error) { - // Collect all time values and ensure no duplicates - timeSet := make(map[time.Time]struct{}) - labelKeys := make(map[string]struct{}) // Collect all unique label keys - numericFields := make(map[string]struct{}) // Collect unique numeric field names - - for _, frame := range frames { - for _, field := range frame.Fields { - if field.Type() == data.FieldTypeTime { - for i := 0; i < field.Len(); i++ { - t := field.At(i).(time.Time) - timeSet[t] = struct{}{} - } - } else if field.Type().Numeric() { - numericFields[field.Name] = struct{}{} - if field.Labels != nil { - for key := range field.Labels { - labelKeys[key] = struct{}{} - } - } - } - } - } - - // Create a sorted slice of unique time values - times := make([]time.Time, 0, len(timeSet)) - for t := range timeSet { - times = append(times, t) - } - sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) }) - - // Create output fields: Time, one numeric field per unique numeric name, and label fields - timeField := data.NewField("Time", nil, times) - outputNumericFields := make(map[string]*data.Field) - for name := range numericFields { - outputNumericFields[name] = data.NewField(name, nil, make([]float64, len(times))) - } - outputLabelFields := make(map[string]*data.Field) - for key := range labelKeys { - outputLabelFields[key] = data.NewField(key, nil, make([]string, len(times))) - } - - // Map time to index for quick lookup - timeIndexMap := make(map[time.Time]int, len(times)) - for i, t := range times { - timeIndexMap[t] = i - } - - // Populate output fields - for _, frame := range frames { - var timeField *data.Field - for _, field := range frame.Fields { - if field.Type() == data.FieldTypeTime { - timeField = field - break - } - } - - if timeField == nil { - return nil, fmt.Errorf("no time field found in frame") - } - - for _, field := range frame.Fields { - if field.Type().Numeric() { - for i := 0; i < field.Len(); i++ { - t := timeField.At(i).(time.Time) - val, err := field.FloatAt(i) - if err != nil { - val = 0 // Default value for missing data - } - idx := timeIndexMap[t] - if outputField, exists := outputNumericFields[field.Name]; exists { - outputField.Set(idx, val) - } - - // Add labels for the numeric field - for key, value := range field.Labels { - if outputField, exists := outputLabelFields[key]; exists { - outputField.Set(idx, value) - } - } - } - } - } - } - - // Build the output frame - outputFields := []*data.Field{timeField} - for _, field := range outputNumericFields { - outputFields = append(outputFields, field) - } - for _, field := range outputLabelFields { - outputFields = append(outputFields, field) - } - outputFrame := data.NewFrame("time_series_long", outputFields...) - - // Set metadata - if outputFrame.Meta == nil { - outputFrame.Meta = &data.FrameMeta{} - } - outputFrame.Meta.Type = data.FrameTypeTimeSeriesLong - - return data.Frames{outputFrame}, nil -} - -func convertTimeSeriesWideToTimeSeriesLong(frames data.Frames) (data.Frames, error) { - // Wide should only be one frame - if len(frames) != 1 { - return nil, fmt.Errorf("expected exactly one frame for wide format, but got %d", len(frames)) - } - inputFrame := frames[0] - longFrame, err := data.WideToLong(inputFrame) - if err != nil { - return nil, fmt.Errorf("failed to convert wide time series to long timeseries for sql expression: %w", err) - } - return data.Frames{longFrame}, nil -} - -func getToLongConversionFunc(inputType data.FrameType) func(data.Frames) (data.Frames, error) { - switch inputType { - case data.FrameTypeNumericMulti: - return convertNumericMultiToNumericLong - case data.FrameTypeNumericWide: - return convertNumericWideToNumericLong - case data.FrameTypeTimeSeriesMulti: - return convertTimeSeriesMultiToTimeSeriesLong - case data.FrameTypeTimeSeriesWide: - return convertTimeSeriesWideToTimeSeriesLong - default: - return convertErr - } -} - -func convertErr(_ data.Frames) (data.Frames, error) { - return nil, fmt.Errorf("unsupported input type for SQL expression") -} - -func supportedToLongConversion(inputType data.FrameType) bool { - switch inputType { - case data.FrameTypeNumericMulti, data.FrameTypeNumericWide: - return true - case data.FrameTypeTimeSeriesMulti, data.FrameTypeTimeSeriesWide: - return true - default: - return false - } -} diff --git a/pkg/expr/convert_to_long_test.go b/pkg/expr/convert_to_long_test.go deleted file mode 100644 index 291fdb62f17..00000000000 --- a/pkg/expr/convert_to_long_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package expr - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/stretchr/testify/require" -) - -func TestConvertNumericMultiToLong(t *testing.T) { - input := data.Frames{ - data.NewFrame("test", - data.NewField("Value", data.Labels{"city": "MIA"}, []int64{5})), - data.NewFrame("test", - data.NewField("Value", data.Labels{"city": "LGA"}, []int64{7}), - ), - } - expectedFrame := data.NewFrame("", - data.NewField("Value", nil, []int64{5, 7}), - data.NewField("city", nil, []string{"MIA", "LGA"}), - ) - output, err := convertNumericMultiToNumericLong(input) - require.NoError(t, err) - - if diff := cmp.Diff(expectedFrame, output[0], data.FrameTestCompareOptions()...); diff != "" { - require.FailNowf(t, "Result mismatch (-want +got):%s\n", diff) - } -} - -func TestConvertNumericWideToLong(t *testing.T) { - input := data.Frames{ - data.NewFrame("test", - data.NewField("Value", data.Labels{"city": "MIA"}, []int64{5}), - data.NewField("Value", data.Labels{"city": "LGA"}, []int64{7}), - ), - } - expectedFrame := data.NewFrame("", - data.NewField("Value", nil, []int64{5, 7}), - data.NewField("city", nil, []string{"MIA", "LGA"}), - ) - output, err := convertNumericWideToNumericLong(input) - require.NoError(t, err) - - if diff := cmp.Diff(expectedFrame, output[0], data.FrameTestCompareOptions()...); diff != "" { - require.FailNowf(t, "Result mismatch (-want +got):%s\n", diff) - } -} diff --git a/pkg/expr/nodes.go b/pkg/expr/nodes.go index dea3b10e659..141ce36e3af 100644 --- a/pkg/expr/nodes.go +++ b/pkg/expr/nodes.go @@ -429,7 +429,7 @@ func (dn *DSNode) Execute(ctx context.Context, now time.Time, _ mathexp.Vars, s } if needsConversion { - convertedFrames, err := ConvertToLong(dataFrames) + convertedFrames, err := ConvertToFullLong(dataFrames) if err != nil { return result, fmt.Errorf("failed to convert data frames to long format for sql: %w", err) } diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index a575dbab08b..2384349a25c 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -76,7 +76,7 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go v1.55.6 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 4d7afcb8d52..3f1f2a91fb6 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -735,8 +735,8 @@ github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 689d9b2d97b..118beb80e1f 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -60,7 +60,7 @@ require ( github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go v1.55.6 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 77e90d5ecf1..fad5380cff8 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -714,8 +714,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ= github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= From 82700adb1f2d2b4d3602abb5cfb31341fa5682e3 Mon Sep 17 00:00:00 2001 From: Ihor Yeromin Date: Mon, 24 Mar 2025 23:03:36 +0200 Subject: [PATCH 47/86] TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling --- .../src/components/Table/TableNG/Cells/HeaderCell.tsx | 6 ++++++ .../grafana-ui/src/components/Table/TableNG/TableNG.tsx | 4 ++++ packages/grafana-ui/src/components/Table/TableNG/utils.ts | 1 + 3 files changed, 11 insertions(+) diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/HeaderCell.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/HeaderCell.tsx index b6a464a6e45..824a71f9186 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/HeaderCell.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/HeaderCell.tsx @@ -6,6 +6,7 @@ import { Column, SortDirection } from 'react-data-grid'; import { Field, GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '../../../../themes'; +import { getFieldTypeIcon } from '../../../../types'; import { Icon } from '../../../Icon/Icon'; import { Filter } from '../Filter/Filter'; import { TableColumnResizeActionCallback, FilterType, TableRow, TableSummaryRow } from '../types'; @@ -24,6 +25,7 @@ interface HeaderCellProps { headerCellRefs: React.MutableRefObject>; crossFilterOrder: React.MutableRefObject; crossFilterRows: React.MutableRefObject<{ [key: string]: TableRow[] }>; + showTypeIcons?: boolean; } const HeaderCell: React.FC = ({ @@ -40,6 +42,7 @@ const HeaderCell: React.FC = ({ headerCellRefs, crossFilterOrder, crossFilterRows, + showTypeIcons, }) => { const styles = useStyles2(getStyles); const headerRef = useRef(null); @@ -106,6 +109,7 @@ const HeaderCell: React.FC = ({ }} >
@@ -275,6 +278,9 @@ const getStyles = (theme: GrafanaTheme2) => { rowBody, label: css({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), lineHeight: ROW_HEIGHT + 'px', flexGrow: 1, minWidth: 0, diff --git a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx index f5c90e5f403..226583c54e1 100644 --- a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx +++ b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx @@ -42,6 +42,12 @@ describe('NestedFolderPicker', () => { }); }), + http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => { + return HttpResponse.json({ + items: [], + }); + }), + http.get('/api/folders', ({ request }) => { const url = new URL(request.url); const parentUid = url.searchParams.get('parentUid') ?? undefined; diff --git a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx index 2656b96b6a2..83eedefef29 100644 --- a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx +++ b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { Alert, Icon, Input, LoadingBar, useStyles2 } from '@grafana/ui'; +import { Alert, Icon, Input, LoadingBar, Stack, Text, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { skipToken, useGetFolderQuery } from 'app/features/browse-dashboards/api/browseDashboardsAPI'; import { DashboardViewItemWithUIItems, DashboardsTreeItem } from 'app/features/browse-dashboards/types'; @@ -16,6 +16,7 @@ import { queryResultToViewItem } from 'app/features/search/service/utils'; import { DashboardViewItem } from 'app/features/search/types'; import { PermissionLevelString } from 'app/types'; +import { FolderRepo } from './FolderRepo'; import { getDOMId, NestedFolderList } from './NestedFolderList'; import Trigger from './Trigger'; import { ROOT_FOLDER_ITEM, useFoldersQuery } from './useFoldersQuery'; @@ -266,10 +267,20 @@ export function NestedFolderPicker({ label = 'Dashboards'; } + // Display the folder name and provisioning status when the picker is closed + const labelComponent = label ? ( + + {label} + + + ) : ( + '' + ); + if (!overlayOpen) { return ( { const folderIsOpen = openFolders[item.uid]; - const flatItem: DashboardsTreeItem = { isOpen: Boolean(folderIsOpen), level: level, @@ -154,6 +153,7 @@ export function useFoldersQuery( kind: 'folder' as const, title: item.title, uid: item.uid, + managedBy: item.managedBy, }, }; diff --git a/public/app/features/alerting/unified/mocks/server/handlers/provisioning.ts b/public/app/features/alerting/unified/mocks/server/handlers/provisioning.ts index 44714487295..6d92f279f5c 100644 --- a/public/app/features/alerting/unified/mocks/server/handlers/provisioning.ts +++ b/public/app/features/alerting/unified/mocks/server/handlers/provisioning.ts @@ -17,5 +17,11 @@ const exportMuteTimingsHandler = () => http.get('/api/v1/provisioning/mute-timin const exportSpecificMuteTimingsHandler = () => http.get('/api/v1/provisioning/mute-timings/:name/export', getProvisioningHelper); -const handlers = [exportMuteTimingsHandler(), exportSpecificMuteTimingsHandler()]; +// Used by folder picker to load provisioning settings +const provisioningSettingsHandler = () => + http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => + HttpResponse.json({ items: [] }) + ); + +const handlers = [exportMuteTimingsHandler(), exportSpecificMuteTimingsHandler(), provisioningSettingsHandler()]; export default handlers; diff --git a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx index 467786f7181..36182de2d44 100644 --- a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx +++ b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx @@ -137,6 +137,11 @@ describe('browse-dashboards BrowseDashboardsPage', () => { return HttpResponse.json({ sortOptions: [], }); + }), + http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => { + return HttpResponse.json({ + items: [], + }); }) ); server.listen(); diff --git a/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx b/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx index 12b31ff4974..006238e269b 100644 --- a/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx +++ b/public/app/features/browse-dashboards/BrowseDashboardsPage.tsx @@ -5,13 +5,15 @@ import AutoSizer from 'react-virtualized-auto-sizer'; import { GrafanaTheme2 } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; -import { LinkButton, FilterInput, useStyles2 } from '@grafana/ui'; +import { LinkButton, FilterInput, useStyles2, Text, Stack } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { getConfig } from 'app/core/config'; import { Trans } from 'app/core/internationalization'; import { useDispatch } from 'app/types'; +import { FolderRepo } from '../../core/components/NestedFolderPicker/FolderRepo'; import { contextSrv } from '../../core/services/context_srv'; +import { ManagerKind } from '../apiserver/types'; import { buildNavModel, getDashboardsTabID } from '../folders/state/navModel'; import { useSearchStateManager } from '../search/state/SearchStateManager'; import { getSearchPlaceholder } from '../search/tempI18nPhrases'; @@ -90,9 +92,9 @@ const BrowseDashboardsPage = memo(() => { const { canEditFolders, canEditDashboards, canCreateDashboards, canCreateFolders } = getFolderPermissions(folder); const hasAdminRights = contextSrv.hasRole('Admin') || contextSrv.isGrafanaAdmin; - - const showEditTitle = canEditFolders && folderUID; - const canSelect = canEditFolders || canEditDashboards; + const isProvisionedFolder = folder?.managedBy === ManagerKind.Repo; + const showEditTitle = canEditFolders && folderUID && !isProvisionedFolder; + const canSelect = (canEditFolders || canEditDashboards) && !isProvisionedFolder; const onEditTitle = async (newValue: string) => { if (folderDTO) { const result = await saveFolder({ @@ -117,11 +119,21 @@ const BrowseDashboardsPage = memo(() => { origin: window.location.pathname === getConfig().appSubUrl + '/dashboards' ? 'Dashboards' : 'Folder view', }); }; + + const renderTitle = (title: string) => { + return ( + + {title} + + ); + }; + return ( {config.featureToggles.dashboardRestore && hasAdminRights && ( diff --git a/public/app/features/browse-dashboards/api/services.ts b/public/app/features/browse-dashboards/api/services.ts index 1c4c8a47e15..c5eb2375b65 100644 --- a/public/app/features/browse-dashboards/api/services.ts +++ b/public/app/features/browse-dashboards/api/services.ts @@ -39,6 +39,7 @@ export async function listFolders( title: item.title, parentTitle, parentUID, + managedBy: item.managedBy, // URLs from the backend come with subUrlPrefix already included, so match that behaviour here url: isSharedWithMe(item.uid) ? undefined : getFolderURL(item.uid), diff --git a/public/app/features/browse-dashboards/components/CheckboxCell.tsx b/public/app/features/browse-dashboards/components/CheckboxCell.tsx index 26325c23f49..96a36b8e9bb 100644 --- a/public/app/features/browse-dashboards/components/CheckboxCell.tsx +++ b/public/app/features/browse-dashboards/components/CheckboxCell.tsx @@ -5,6 +5,7 @@ import { selectors } from '@grafana/e2e-selectors'; import { Checkbox, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; +import { ManagerKind } from '../../apiserver/types'; import { DashboardsTreeCellProps, SelectionState } from '../types'; import { isSharedWithMe } from './utils'; @@ -41,6 +42,7 @@ export default function CheckboxCell({ value={state === SelectionState.Selected} indeterminate={state === SelectionState.Mixed} onChange={(ev) => onItemSelectionChange?.(item, ev.currentTarget.checked)} + disabled={item.managedBy === ManagerKind.Repo} /> ); } diff --git a/public/app/features/browse-dashboards/components/CreateNewButton.tsx b/public/app/features/browse-dashboards/components/CreateNewButton.tsx index ed06d0b14a2..600d0038224 100644 --- a/public/app/features/browse-dashboards/components/CreateNewButton.tsx +++ b/public/app/features/browse-dashboards/components/CreateNewButton.tsx @@ -5,6 +5,7 @@ import { locationUtil } from '@grafana/data'; import { config, locationService, reportInteraction } from '@grafana/runtime'; import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; +import { useIsProvisionedInstance } from 'app/features/provisioning/hooks/useIsProvisionedInstance'; import { getImportPhrase, getNewDashboardPhrase, @@ -13,9 +14,11 @@ import { } from 'app/features/search/tempI18nPhrases'; import { FolderDTO } from 'app/types'; +import { ManagerKind } from '../../apiserver/types'; import { useNewFolderMutation } from '../api/browseDashboardsAPI'; import { NewFolderForm } from './NewFolderForm'; +import { NewProvisionedFolderForm } from './NewProvisionedFolderForm'; interface Props { parentFolder?: FolderDTO; @@ -29,6 +32,7 @@ export default function CreateNewButton({ parentFolder, canCreateDashboard, canC const [newFolder] = useNewFolderMutation(); const [showNewFolderDrawer, setShowNewFolderDrawer] = useState(false); const notifyApp = useAppNotification(); + const isProvisionedInstance = useIsProvisionedInstance(); const onCreateFolder = async (folderName: string) => { try { @@ -102,7 +106,15 @@ export default function CreateNewButton({ parentFolder, canCreateDashboard, canC onClose={() => setShowNewFolderDrawer(false)} size="sm" > - setShowNewFolderDrawer(false)} /> + {parentFolder?.managedBy === ManagerKind.Repo || isProvisionedInstance ? ( + setShowNewFolderDrawer(false)} + onCancel={() => setShowNewFolderDrawer(false)} + parentFolder={parentFolder} + /> + ) : ( + setShowNewFolderDrawer(false)} /> + )} )} diff --git a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx index 1c4f3984d86..b47bd044d5f 100644 --- a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx +++ b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx @@ -5,9 +5,11 @@ import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui'; import { Permissions } from 'app/core/components/AccessControl'; import { appEvents } from 'app/core/core'; import { t, Trans } from 'app/core/internationalization'; +import { ProvisionedResourceDeleteModal } from 'app/features/dashboard-scene/saving/provisioned/ProvisionedResourceDeleteModal'; import { FolderDTO } from 'app/types'; import { ShowModalReactEvent } from 'app/types/events'; +import { ManagerKind } from '../../apiserver/types'; import { useDeleteFolderMutation, useMoveFolderMutation } from '../api/browseDashboardsAPI'; import { getFolderPermissions } from '../permissions'; @@ -24,8 +26,9 @@ export function FolderActionsButton({ folder }: Props) { const [moveFolder] = useMoveFolderMutation(); const [deleteFolder] = useDeleteFolderMutation(); const { canEditFolders, canDeleteFolders, canViewPermissions, canSetPermissions } = getFolderPermissions(folder); - // Can only move folders when nestedFolders is enabled - const canMoveFolder = config.featureToggles.nestedFolders && canEditFolders; + const isProvisionedFolder = folder.managedBy === ManagerKind.Repo; + // Can only move folders when nestedFolders is enabled and the folder is not provisioned + const canMoveFolder = config.featureToggles.nestedFolders && canEditFolders && !isProvisionedFolder; const onMove = async (destinationUID: string) => { await moveFolder({ folder, destinationUID }); @@ -86,6 +89,17 @@ export function FolderActionsButton({ folder }: Props) { ); }; + const showDeleteProvisionedModal = () => { + appEvents.publish( + new ShowModalReactEvent({ + component: ProvisionedResourceDeleteModal, + props: { + resource: folder, + }, + }) + ); + }; + const managePermissionsLabel = t('browse-dashboards.folder-actions-button.manage-permissions', 'Manage permissions'); const moveLabel = t('browse-dashboards.folder-actions-button.move', 'Move'); const deleteLabel = t('browse-dashboards.folder-actions-button.delete', 'Delete'); @@ -94,7 +108,13 @@ export function FolderActionsButton({ folder }: Props) { {canViewPermissions && setShowPermissionsDrawer(true)} label={managePermissionsLabel} />} {canMoveFolder && } - {canDeleteFolders && } + {canDeleteFolders && ( + + )} ); diff --git a/public/app/features/browse-dashboards/components/NameCell.tsx b/public/app/features/browse-dashboards/components/NameCell.tsx index 6bc1b9f680b..56050295269 100644 --- a/public/app/features/browse-dashboards/components/NameCell.tsx +++ b/public/app/features/browse-dashboards/components/NameCell.tsx @@ -9,6 +9,7 @@ import { t, Trans } from 'app/core/internationalization'; import { getIconForItem } from 'app/features/search/service/utils'; import { Indent } from '../../../core/components/Indent/Indent'; +import { FolderRepo } from '../../../core/components/NestedFolderPicker/FolderRepo'; import { useChildrenByParentUIDState } from '../state'; import { DashboardsTreeCellProps } from '../types'; @@ -102,6 +103,8 @@ export function NameCell({ row: { original: data }, onFolderClick, treeID }: Nam item.title )} + +
); diff --git a/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.test.tsx b/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.test.tsx new file mode 100644 index 00000000000..f289e2f1fbb --- /dev/null +++ b/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.test.tsx @@ -0,0 +1,450 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { AppEvents } from '@grafana/data'; +import { getAppEvents } from '@grafana/runtime'; +import { useGetFolderQuery } from 'app/api/clients/folder'; +import { useCreateRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning'; +import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv'; +import { usePullRequestParam, useRepositoryList } from 'app/features/provisioning/hooks'; + +import { FolderDTO } from '../../../types'; + +import { NewProvisionedFolderForm } from './NewProvisionedFolderForm'; + +jest.mock('@grafana/runtime', () => { + const actual = jest.requireActual('@grafana/runtime'); + return { + ...actual, + getAppEvents: jest.fn(), + locationService: { + partial: jest.fn(), + }, + config: { + ...actual.config, + }, + }; +}); + +jest.mock('app/features/manage-dashboards/services/ValidationSrv', () => { + return { + validationSrv: { + validateNewFolderName: jest.fn(), + }, + }; +}); + +jest.mock('app/api/clients/provisioning', () => { + return { + useCreateRepositoryFilesWithPathMutation: jest.fn(), + }; +}); + +jest.mock('app/api/clients/folder', () => { + return { + useGetFolderQuery: jest.fn(), + }; +}); + +jest.mock('app/features/provisioning/hooks', () => { + return { + usePullRequestParam: jest.fn(), + useRepositoryList: jest.fn(), + }; +}); + +jest.mock('react-router-dom-v5-compat', () => { + const actual = jest.requireActual('react-router-dom-v5-compat'); + return { + ...actual, + useNavigate: () => jest.fn(), + }; +}); + +// Mock the defaults +jest.mock('../../dashboard-scene/saving/provisioned/defaults', () => { + return { + getDefaultWorkflow: jest.fn().mockReturnValue('write'), + getWorkflowOptions: jest.fn().mockReturnValue([ + { label: 'Commit directly', value: 'write' }, + { label: 'Create a branch', value: 'branch' }, + ]), + }; +}); + +interface Props { + onSubmit: () => void; + onCancel: () => void; + parentFolder: FolderDTO; +} + +function setup(props: Partial = {}) { + const user = userEvent.setup(); + + const defaultProps: Props = { + onSubmit: jest.fn(), + onCancel: jest.fn(), + parentFolder: { + id: 1, + uid: 'folder-uid', + title: 'Parent Folder', + url: '/dashboards/f/folder-uid', + hasAcl: false, + canSave: true, + canEdit: true, + canAdmin: true, + canDelete: true, + repository: { + name: 'test-repo', + type: 'github', + }, + } as unknown as FolderDTO, + ...props, + }; + + return { + user, + ...render(), + props: defaultProps, + }; +} + +const mockRequest = { + isSuccess: false, + isError: false, + isLoading: false, + error: null, + data: { resource: { upsert: { metadata: { name: 'new-folder' } } } }, +}; + +describe('NewProvisionedFolderForm', () => { + beforeEach(() => { + jest.clearAllMocks(); + + // Setup default mocks + const mockAppEvents = { + publish: jest.fn(), + }; + (getAppEvents as jest.Mock).mockReturnValue(mockAppEvents); + + (useRepositoryList as jest.Mock).mockReturnValue([ + [ + { + metadata: { + name: 'test-repo', + }, + spec: { + title: 'Test Repository', + type: 'github', + github: { + url: 'https://github.com/grafana/grafana', + branch: 'main', + }, + workflows: [{ name: 'default', path: 'workflows/default.yaml' }], + }, + }, + ], + false, + ]); + + // Mock useGetFolderQuery + (useGetFolderQuery as jest.Mock).mockReturnValue({ + data: { + metadata: { + annotations: { + 'source.path': '/dashboards', + }, + }, + }, + isLoading: false, + isError: false, + }); + + // Mock usePullRequestParam + (usePullRequestParam as jest.Mock).mockReturnValue(null); + + // Mock useCreateRepositoryFilesWithPathMutation + const mockCreate = jest.fn(); + + (useCreateRepositoryFilesWithPathMutation as jest.Mock).mockReturnValue([mockCreate, mockRequest]); + + (validationSrv.validateNewFolderName as jest.Mock).mockResolvedValue(true); + }); + + it('should render the form with correct fields', () => { + setup(); + + // Check if form elements are rendered + expect(screen.getByRole('textbox', { name: /folder name/i })).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: /comment/i })).toBeInTheDocument(); + expect(screen.getByRole('radiogroup')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /^create$/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); + }); + + it('should show loading state when repository data is loading', () => { + (useRepositoryList as jest.Mock).mockReturnValue([[], true]); + + setup(); + + expect(screen.getByTestId('Spinner')).toBeInTheDocument(); + }); + + it('should show error when repository is not found', () => { + (useRepositoryList as jest.Mock).mockReturnValue([null, false]); + + setup(); + + expect(screen.getByText('Repository not found')).toBeInTheDocument(); + }); + + it('should show branch field when branch workflow is selected', async () => { + const { user } = setup(); + + expect(screen.queryByRole('textbox', { name: /branch/i })).not.toBeInTheDocument(); + + const branchOption = screen.getByRole('radio', { name: /create a branch/i }); + await user.click(branchOption); + + expect(screen.getByRole('textbox', { name: /branch/i })).toBeInTheDocument(); + }); + + it('should validate folder name', async () => { + (validationSrv.validateNewFolderName as jest.Mock).mockRejectedValue(new Error('Folder name already exists')); + + const { user } = setup(); + + const folderNameInput = screen.getByRole('textbox', { name: /folder name/i }); + await user.clear(folderNameInput); + await user.type(folderNameInput, 'Existing Folder'); + + // Submit the form + const submitButton = screen.getByRole('button', { name: /^create$/i }); + await user.click(submitButton); + + // Wait for validation error to appear + await waitFor(() => { + expect(screen.getByText('Folder name already exists')).toBeInTheDocument(); + }); + }); + + it('should validate branch name', async () => { + const { user } = setup(); + + // Select branch workflow + const branchOption = screen.getByRole('radio', { name: /create a branch/i }); + await user.click(branchOption); + + // Enter invalid branch name + const branchInput = screen.getByRole('textbox', { name: /branch/i }); + await user.clear(branchInput); + await user.type(branchInput, 'invalid//branch'); + + // Submit the form + const submitButton = screen.getByRole('button', { name: /^create$/i }); + await user.click(submitButton); + + // Wait for validation error to appear + await waitFor(() => { + expect(screen.getByText('Invalid branch name.')).toBeInTheDocument(); + }); + }); + + it('should create folder successfully', async () => { + const mockCreate = jest.fn(); + (useCreateRepositoryFilesWithPathMutation as jest.Mock).mockReturnValue([ + mockCreate, + { + ...mockRequest, + isSuccess: true, + isError: false, + isLoading: false, + error: null, + }, + ]); + + const { user, props } = setup(); + + const folderNameInput = screen.getByRole('textbox', { name: /folder name/i }); + const commentInput = screen.getByRole('textbox', { name: /comment/i }); + + await user.clear(folderNameInput); + await user.type(folderNameInput, 'New Test Folder'); + + await user.clear(commentInput); + await user.type(commentInput, 'Creating a new test folder'); + + // Submit the form + const submitButton = screen.getByRole('button', { name: /^create$/i }); + await user.click(submitButton); + + // Check if create was called with correct parameters + await waitFor(() => { + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + ref: undefined, // write workflow uses undefined ref + name: 'test-repo', + message: 'Creating a new test folder', + body: { + title: 'New Test Folder', + type: 'folder', + }, + }) + ); + }); + + // Check if onSubmit was called + expect(props.onSubmit).toHaveBeenCalled(); + }); + + it('should create folder with branch workflow', async () => { + const mockCreate = jest.fn(); + (useCreateRepositoryFilesWithPathMutation as jest.Mock).mockReturnValue([ + mockCreate, + { + ...mockRequest, + isSuccess: true, + isError: false, + isLoading: false, + error: null, + }, + ]); + + const { user } = setup(); + + // Fill form + const folderNameInput = screen.getByRole('textbox', { name: /folder name/i }); + await user.clear(folderNameInput); + await user.type(folderNameInput, 'Branch Folder'); + + // Select branch workflow + const branchOption = screen.getByRole('radio', { name: /create a branch/i }); + await user.click(branchOption); + + // Enter branch name + const branchInput = screen.getByRole('textbox', { name: /branch/i }); + await user.clear(branchInput); + await user.type(branchInput, 'feature/new-folder'); + + // Submit the form + const submitButton = screen.getByRole('button', { name: /^create$/i }); + await user.click(submitButton); + + // Check if create was called with correct parameters + await waitFor(() => { + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + ref: 'feature/new-folder', + name: 'test-repo', + message: 'Create folder: Branch Folder', + body: { + title: 'Branch Folder', + type: 'folder', + }, + }) + ); + }); + }); + + it('should show error when folder creation fails', async () => { + const mockCreate = jest.fn(); + (useCreateRepositoryFilesWithPathMutation as jest.Mock).mockReturnValue([ + mockCreate, + { + ...mockRequest, + isSuccess: false, + isError: true, + isLoading: false, + error: 'Failed to create folder', + }, + ]); + + const { user } = setup(); + + // Fill form + const folderNameInput = screen.getByRole('textbox', { name: /folder name/i }); + await user.clear(folderNameInput); + await user.type(folderNameInput, 'Error Folder'); + + // Submit the form + const submitButton = screen.getByRole('button', { name: /^create$/i }); + await user.click(submitButton); + + // Check if error alert was published + await waitFor(() => { + const appEvents = getAppEvents(); + expect(appEvents.publish).toHaveBeenCalledWith({ + type: AppEvents.alertError.name, + payload: ['Error creating folder', 'Failed to create folder'], + }); + }); + }); + + it('should disable create button when form is submitting', async () => { + (useCreateRepositoryFilesWithPathMutation as jest.Mock).mockReturnValue([ + jest.fn(), + { + ...mockRequest, + isSuccess: false, + isError: false, + isLoading: true, + error: null, + }, + ]); + + setup(); + + // Create button should be disabled and show loading text + const createButton = screen.getByRole('button', { name: /creating/i }); + expect(createButton).toBeDisabled(); + expect(createButton).toHaveTextContent('Creating...'); + }); + + it('should show PR link when PR URL is available', () => { + (usePullRequestParam as jest.Mock).mockReturnValue('https://github.com/grafana/grafana/pull/1234'); + + setup(); + + // PR alert should be visible - use text content instead of role + expect(screen.getByText('Pull request created')).toBeInTheDocument(); + expect(screen.getByRole('link')).toHaveTextContent('https://github.com/grafana/grafana/pull/1234'); + }); + + it('should call onCancel when cancel button is clicked', async () => { + const { user, props } = setup(); + + // Click cancel button + const cancelButton = screen.getByRole('button', { name: /cancel/i }); + await user.click(cancelButton); + + // Check if onCancel was called + expect(props.onCancel).toHaveBeenCalled(); + }); + + it('should show read-only alert when repository has no workflows', () => { + // Mock repository with empty workflows array + (useRepositoryList as jest.Mock).mockReturnValue([ + [ + { + metadata: { + name: 'test-repo', + }, + spec: { + type: 'github', + github: { + url: 'https://github.com/grafana/grafana', + branch: 'main', + }, + workflows: [], // Empty workflows array + }, + }, + ], + false, + ]); + + setup(); + + // Read-only alert should be visible + expect(screen.getByText('This repository is read only')).toBeInTheDocument(); + }); +}); diff --git a/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.tsx b/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.tsx new file mode 100644 index 00000000000..5414e5ac9c3 --- /dev/null +++ b/public/app/features/browse-dashboards/components/NewProvisionedFolderForm.tsx @@ -0,0 +1,249 @@ +import { skipToken } from '@reduxjs/toolkit/query'; +import { useEffect } from 'react'; +import { Controller, useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom-v5-compat'; + +import { AppEvents } from '@grafana/data'; +import { getAppEvents } from '@grafana/runtime'; +import { Alert, Button, Field, Input, RadioButtonGroup, Spinner, Stack, TextArea } from '@grafana/ui'; +import { useGetFolderQuery } from 'app/api/clients/folder'; +import { useCreateRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning'; +import { AnnoKeyManagerIdentity, AnnoKeySourcePath, Resource } from 'app/features/apiserver/types'; +import { getDefaultWorkflow, getWorkflowOptions } from 'app/features/dashboard-scene/saving/provisioned/defaults'; +import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv'; +import { PROVISIONING_URL } from 'app/features/provisioning/constants'; +import { usePullRequestParam, useRepositoryList } from 'app/features/provisioning/hooks'; +import { WorkflowOption } from 'app/features/provisioning/types'; +import { validateBranchName } from 'app/features/provisioning/utils/git'; +import { FolderDTO } from 'app/types'; + +type FormData = { + ref?: string; + path: string; + comment?: string; + repo: string; + workflow?: WorkflowOption; + title: string; +}; + +interface Props { + onSubmit: () => void; + onCancel: () => void; + parentFolder?: FolderDTO; +} + +const initialFormValues: Partial = { + title: '', + comment: '', + ref: `folder/${Date.now()}`, +}; + +export function NewProvisionedFolderForm({ onSubmit, onCancel, parentFolder }: Props) { + const [items, isLoading] = useRepositoryList(); + const prURL = usePullRequestParam(); + const navigate = useNavigate(); + const [create, request] = useCreateRepositoryFilesWithPathMutation(); + + // Get k8s folder data, necessary to get parent folder path + const folderQuery = useGetFolderQuery(parentFolder ? { name: parentFolder.uid } : skipToken); + const repositoryName = folderQuery.data?.metadata?.annotations?.[AnnoKeyManagerIdentity]; + if (!items && !isLoading) { + return ; + } + + const repository = repositoryName ? items?.find((item) => item?.metadata?.name === repositoryName) : items?.[0]; + const repositoryConfig = repository?.spec; + const isGitHub = Boolean(repositoryConfig?.github); + + const { + register, + handleSubmit, + watch, + formState: { errors }, + control, + setValue, + } = useForm({ defaultValues: { ...initialFormValues, workflow: getDefaultWorkflow(repositoryConfig) } }); + + const [workflow, ref] = watch(['workflow', 'ref']); + + useEffect(() => { + setValue('workflow', getDefaultWorkflow(repositoryConfig)); + }, [repositoryConfig, setValue]); + + useEffect(() => { + const appEvents = getAppEvents(); + if (request.isSuccess) { + onSubmit(); + + appEvents.publish({ + type: AppEvents.alertSuccess.name, + payload: ['Folder created successfully'], + }); + + const folder = request.data.resource?.upsert as Resource; + if (folder?.metadata?.name) { + navigate(`/dashboards/f/${folder?.metadata?.name}/`); + return; + } + + let url = `${PROVISIONING_URL}/${repositoryName}/file/${request.data.path}`; + if (request.data.ref?.length) { + url += '?ref=' + request.data.ref; + } + navigate(url); + } else if (request.isError) { + appEvents.publish({ + type: AppEvents.alertError.name, + payload: ['Error creating folder', request.error], + }); + } + }, [ + request.isSuccess, + request.isError, + request.error, + onSubmit, + ref, + request.data, + workflow, + navigate, + repositoryName, + ]); + + if (isLoading || folderQuery.isLoading) { + return ; + } + + const validateFolderName = async (folderName: string) => { + try { + await validationSrv.validateNewFolderName(folderName); + return true; + } catch (e) { + if (e instanceof Error) { + return e.message; + } + return 'Invalid folder name'; + } + }; + + const doSave = async ({ ref, title, workflow, comment }: FormData) => { + const repoName = repository?.metadata?.name; + if (!title || !repoName) { + return; + } + const basePath = folderQuery.data?.metadata?.annotations?.[AnnoKeySourcePath] ?? ''; + + // Convert folder title to filename format (lowercase, replace spaces with hyphens) + const titleInFilenameFormat = title + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, ''); + + const prefix = basePath ? `${basePath}/` : ''; + const path = `${prefix}${titleInFilenameFormat}/`; + + const folderModel = { + title, + type: 'folder', + }; + + if (workflow === 'write') { + ref = undefined; + } + + create({ + ref, + name: repoName, + path, + message: comment || `Create folder: ${title}`, + body: folderModel, + }); + }; + + return ( + + + {!repositoryConfig?.workflows.length && ( + + If you have direct access to the target, copy the JSON and paste it there. + + )} + + + + + + +