diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index fa2adef5..4d4a48bf 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -9,43 +9,18 @@ on: jobs: test: - strategy: - matrix: - go: [ {version: 1.23.0, token: 1}, {version: 1.23.0, token: 0}] - grafana: [ 10.1.4 ] - - env: - GRAFANA_INTEGRATION: 1 - TEST_TOKEN_CONFIG: "${{ matrix.go.token }}" - runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: ${{ matrix.go.version }} + go-version: "1.23.0" + - name: Verify go version run: go version - - uses: actions/cache@v4 - with: - path: | - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Validate ENV Value - shell: bash - run: | - echo "token IS $TEST_TOKEN_CONFIG" - - name: Calc coverage - if: "${{ matrix.go.version == '1.23.0' && matrix.grafana == '10.1.4' && matrix.go.token == '0' }}" - run: | - go test -v -covermode=atomic -coverprofile=coverage.out ./... - - name: Convert coverage.out to coverage.lcov - if: "${{ matrix.go.version == '1.23.0' && matrix.grafana == '10.1.4' && matrix.go.token == '0' }}" - uses: jandelgado/gcov2lcov-action@v1.0.9 - - name: Test - if: "${{ matrix.go.token == '1' }}" + - name: Test + env: + ENTERPRISE_LICENSE: ${{ secrets.ENTERPRISE_LICENSE }} run: go test -v ./... + diff --git a/Taskfile.yml b/Taskfile.yml index c493c968..91872746 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -111,7 +111,6 @@ tasks: - go test -v -coverpkg=./... -covermode=atomic -coverprofile=coverage.out ./... - go tool cover -html=coverage.out env: - GRAFANA_INTEGRATION: "1" TEST_TOKEN_CONFIG: "0" test_tokens: desc: "test Token Based Only" @@ -119,7 +118,6 @@ tasks: - go test -v -coverpkg=./... -covermode=atomic -coverprofile=coverage.out ./... - go tool cover -html=coverage.out env: - GRAFANA_INTEGRATION: "1" TEST_TOKEN_CONFIG: "1" vuln_check: diff --git a/cli/backup/connection_permissions.go b/cli/backup/connection_permissions.go index 1b08025f..986ab669 100644 --- a/cli/backup/connection_permissions.go +++ b/cli/backup/connection_permissions.go @@ -50,17 +50,28 @@ func newConnectionsPermissionListCmd() simplecobra.Commander { slog.Info("Listing Connection Permissions for context", "context", config.Config().GetGDGConfig().GetContext()) rootCmd.TableObj.AppendHeader(table.Row{"id", "uid", "name", "slug", "type", "default", "url"}) connections := rootCmd.GrafanaSvc().ListConnectionPermissions(filters) - _ = connections if len(connections) == 0 { slog.Info("No connections found") } else { - for link, perms := range connections { - url := fmt.Sprintf("%s/datasource/edit/%d", config.Config().GetDefaultGrafanaConfig().URL, link.ID) - rootCmd.TableObj.AppendRow(table.Row{link.ID, link.UID, link.Name, service.GetSlug(link.Name), link.Type, link.IsDefault, url}) - if perms != nil && perms.Enabled { - for _, perm := range perms.Permissions { - rootCmd.TableObj.AppendRow(table.Row{link.ID, link.UID, " PERMISSION-->", perm.PermissionName, perm.Team, perm.UserEmail}) + for _, item := range connections { + rootCmd.TableObj.AppendRow(table.Row{item.Connection.ID, item.Connection.UID, item.Connection.Name, service.GetSlug(item.Connection.Name), item.Connection.Type, item.Connection.IsDefault, getConnectionURL(item.Connection.UID)}) + if item.Permissions != nil { + for _, perm := range item.Permissions { + permissionType := "BuiltinRole" + value := "" + if perm.BuiltInRole != "" { + value = "builtInRole:" + perm.BuiltInRole + } else if perm.UserLogin != "" { + permissionType = "User" + value = "user:" + perm.UserLogin + } else if perm.Team != "" { + permissionType = "Team" + value = "team:" + perm.Team + } else { + permissionType = "unsupported" + } + rootCmd.TableObj.AppendRow(table.Row{item.Connection.ID, item.Connection.UID, " PERMISSION-->", perm.Permission, permissionType, value}) } } } @@ -144,7 +155,7 @@ func newConnectionsPermissionUploadCmd() simplecobra.Commander { }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { slog.Info("Uploading connections permissions") - rootCmd.TableObj.AppendHeader(table.Row{"connection permission"}) + rootCmd.TableObj.AppendHeader(table.Row{"connection permission applied"}) connectionFilter, _ := cd.CobraCommand.Flags().GetString("connection") filters := service.NewConnectionFilter(connectionFilter) connections := rootCmd.GrafanaSvc().UploadConnectionPermissions(filters) diff --git a/cli/backup/connections.go b/cli/backup/connections.go index 64398df7..d24f6b2c 100644 --- a/cli/backup/connections.go +++ b/cli/backup/connections.go @@ -131,8 +131,7 @@ func newListConnectionsCmd() simplecobra.Commander { slog.Info("No connections found") } else { for _, link := range dsListing { - url := fmt.Sprintf("%s/datasource/edit/%d", config.Config().GetDefaultGrafanaConfig().URL, link.ID) - rootCmd.TableObj.AppendRow(table.Row{link.ID, link.UID, link.Name, service.GetSlug(link.Name), link.Type, link.IsDefault, url}) + rootCmd.TableObj.AppendRow(table.Row{link.ID, link.UID, link.Name, service.GetSlug(link.Name), link.Type, link.IsDefault, getConnectionURL(link.UID)}) } rootCmd.Render(cd.CobraCommand, dsListing) } @@ -140,3 +139,8 @@ func newListConnectionsCmd() simplecobra.Commander { }, } } + +func getConnectionURL(uid string) string { + url := config.Config().GetDefaultGrafanaConfig().URL + return fmt.Sprintf("%s/connections/datasources/edit/%s", url, uid) +} diff --git a/cli/test/conections_test.go b/cli/test/conections_test.go index 3613d8df..8afa9abb 100644 --- a/cli/test/conections_test.go +++ b/cli/test/conections_test.go @@ -5,6 +5,7 @@ import ( "github.com/esnet/gdg/cli/support" "github.com/esnet/gdg/internal/service" "github.com/esnet/gdg/internal/service/mocks" + "github.com/esnet/gdg/pkg/test_tooling/common" "github.com/grafana/grafana-openapi-client-go/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -38,7 +39,7 @@ func TestConnectionCommand(t *testing.T) { } r, w, cleanup := InterceptStdout() - err := cli.Execute("testing.yml", []string{"backup", "connections", "list"}, optionMockSvc()) + err := cli.Execute(common.DefaultTestConfig, []string{"backup", "connections", "list"}, optionMockSvc()) assert.Nil(t, err) defer cleanup() w.Close() diff --git a/cli/test/support.go b/cli/test/support.go index 1e0475a1..355ed98b 100644 --- a/cli/test/support.go +++ b/cli/test/support.go @@ -6,6 +6,7 @@ import ( applog "github.com/esnet/gdg/internal/log" "github.com/esnet/gdg/internal/service" "github.com/esnet/gdg/internal/service/mocks" + "github.com/esnet/gdg/pkg/test_tooling/common" "log/slog" "github.com/stretchr/testify/assert" @@ -29,7 +30,7 @@ func setupAndExecuteMockingServices(t *testing.T, process func(mock *mocks.Grafa } r, w, cleanup := InterceptStdout() - data, err := os.ReadFile("../../config/testing.yml") + data, err := os.ReadFile("../../config/" + common.DefaultTestConfig) assert.Nil(t, err) err = process(testSvc, data, optionMockSvc) diff --git a/cli/test/version_test.go b/cli/test/version_test.go index 4d0b5a92..8d977975 100644 --- a/cli/test/version_test.go +++ b/cli/test/version_test.go @@ -7,6 +7,7 @@ import ( "github.com/esnet/gdg/internal/service" "github.com/esnet/gdg/internal/service/mocks" "github.com/esnet/gdg/internal/version" + "github.com/esnet/gdg/pkg/test_tooling/common" "github.com/stretchr/testify/assert" "os" "strings" @@ -43,7 +44,7 @@ func TestVersionErrCommand(t *testing.T) { } path, _ := os.Getwd() fmt.Println(path) - data, err := os.ReadFile("../../config/testing.yml") + data, err := os.ReadFile("../../config/" + common.DefaultTestConfig) assert.Nil(t, err) err = cli.Execute(string(data), []string{"dumb", "dumb"}, optionMockSvc()) diff --git a/config/importer-example.yml b/config/importer-example.yml index 99ae5f7f..122889bb 100644 --- a/config/importer-example.yml +++ b/config/importer-example.yml @@ -111,6 +111,7 @@ contexts: global: debug: true + api_debug: false ignore_ssl_errors: false ##when set to true will ignore invalid SSL errors retry_count: 3 ## Will retry any failed API request up to 3 times. retry_delay: 5s ## will wait for specified duration before trying again. diff --git a/config/testing.yml b/config/testing.yml index 22808194..871e8eca 100644 --- a/config/testing.yml +++ b/config/testing.yml @@ -4,6 +4,7 @@ context_name: testing contexts: testing: output_path: test/data + enterprise_support: true connections: credential_rules: - rules: @@ -18,6 +19,7 @@ contexts: secure_data: "default.json" url: http://localhost:3000 user_name: admin + organization_name: Main Org. password: admin ignore_filters: False # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on watched: @@ -72,8 +74,10 @@ contexts: global: debug: true + api_debug: false ignore_ssl_errors: false ##when set to true will ignore invalid SSL errors - + retry_count: 1 ## Will retry any failed API request up to 3 times. + retry_delay: 1s ## will wait for specified duration before trying again. storage_engine: test: diff --git a/docker-compose.yml b/docker-compose.yml index ebb01412..f689e728 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,11 +9,12 @@ services: - MINIO_ROOT_USER=test - MINIO_ROOT_PASSWORD=secretsss grafana: - image: grafana/grafana:10.0.0-ubuntu + image: grafana/grafana-enterprise:11.1.4-ubuntu ports: - 3000:3000 environment: - GF_INSTALL_PLUGINS=grafana-googlesheets-datasource + - GF_ENTERPRISE_LICENSE_TEXT=${ENTERPRISE_LICENSE} influx: image: influxdb:latest diff --git a/go.mod b/go.mod index 23a95317..011c3b44 100644 --- a/go.mod +++ b/go.mod @@ -4,197 +4,198 @@ go 1.23.0 require ( github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/Masterminds/sprig/v3 v3.2.3 + github.com/Masterminds/sprig/v3 v3.3.0 github.com/avast/retry-go v3.0.0+incompatible - github.com/aws/aws-sdk-go v1.50.0 + github.com/aws/aws-sdk-go-v2 v1.30.4 + github.com/aws/aws-sdk-go-v2/credentials v1.17.30 + github.com/aws/aws-sdk-go-v2/service/s3 v1.61.0 + github.com/aws/smithy-go v1.20.4 github.com/bep/simplecobra v0.4.0 - github.com/carlmjohnson/requests v0.23.5 - github.com/go-openapi/strfmt v0.22.0 + github.com/carlmjohnson/requests v0.24.2 + github.com/go-openapi/strfmt v0.23.0 github.com/google/uuid v1.6.0 - github.com/gosimple/slug v1.13.1 - github.com/grafana/dashboard-linter v0.0.0-20231114210226-c458893a5731 - github.com/grafana/grafana-openapi-client-go v0.0.0-20231219151618-11c46332acad - github.com/jedib0t/go-pretty/v6 v6.4.9 - github.com/lmittmann/tint v1.0.3 + github.com/gosimple/slug v1.14.0 + github.com/grafana/dashboard-linter v0.0.0-20240808111517-eb2bc3ba25e3 + github.com/grafana/grafana-openapi-client-go v0.0.0-20240826142251-d1c93bae4198 + github.com/jedib0t/go-pretty/v6 v6.5.9 + github.com/joho/godotenv v1.5.1 + github.com/lmittmann/tint v1.0.5 github.com/mattn/go-isatty v0.0.20 - github.com/samber/lo v1.39.0 - github.com/sethvargo/go-password v0.2.0 - github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.18.2 + github.com/samber/lo v1.47.0 + github.com/sethvargo/go-password v0.3.1 + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - github.com/testcontainers/testcontainers-go v0.26.0 + github.com/testcontainers/testcontainers-go v0.33.0 github.com/thoas/go-funk v0.9.3 - github.com/tidwall/gjson v1.17.0 + github.com/tidwall/gjson v1.17.3 github.com/tidwall/pretty v1.2.1 github.com/zeitlinger/conflate v0.0.0-20230622100834-279724abda8c - gocloud.dev v0.36.0 - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/mod v0.17.0 + gocloud.dev v0.39.0 + golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 + golang.org/x/mod v0.20.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute v1.24.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.6 // indirect - cloud.google.com/go/storage v1.38.0 // indirect - dario.cat/mergo v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1 // indirect + cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go/auth v0.9.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/iam v1.2.0 // indirect + cloud.google.com/go/storage v1.43.0 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect 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.2.2 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2 v1.24.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect - github.com/aws/aws-sdk-go-v2/config v1.26.2 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.16.13 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 // indirect - github.com/aws/smithy-go v1.19.0 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.31 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/containerd/containerd v1.7.11 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/docker v25.0.6+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.2.0+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-openapi/analysis v0.22.1 // indirect - github.com/go-openapi/errors v0.21.0 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/loads v0.21.5 // indirect - github.com/go-openapi/runtime v0.26.2 // indirect - github.com/go-openapi/spec v0.20.13 // indirect - github.com/go-openapi/swag v0.22.7 // indirect - github.com/go-openapi/validate v0.22.6 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect + github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/loads v0.22.0 // indirect + github.com/go-openapi/runtime v0.28.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/google/wire v0.5.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.1 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/google/wire v0.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // 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/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect + github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // 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/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect - github.com/prometheus/client_golang v1.18.0 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.50.0 // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.57.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/prometheus v0.54.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/shirou/gopsutil/v3 v3.23.11 // indirect + github.com/shirou/gopsutil/v3 v3.24.5 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // 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/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tklauser/go-sysconf v0.3.13 // indirect - github.com/tklauser/numcpus v0.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.mongodb.org/mongo-driver v1.13.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/api v0.167.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect - google.golang.org/grpc v1.62.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/time v0.6.0 // indirect + golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect + google.golang.org/api v0.195.0 // indirect + google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/grpc v1.66.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 401b1c33..963c13cf 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,34 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= -cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.9.2 h1:I+Rq388FYU8QdbVB1IiPd+6KNdrqtAPE/asiKHShBLM= +cloud.google.com/go/auth v0.9.2/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8= +cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= +cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI= +cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +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/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/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1 h1:AMf7YbZOZIW5b66cXNHMWWT/zkjhz5+a+k/3x40EO7E= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1/go.mod h1:uwfk06ZBcvL/g4VHNjurPfVln9NMbsk2XIZxJ+hu81k= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0 h1:Be6KInmFEKV81c0pOAEbRYehLMwmmGI1exuFj248AMk= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0/go.mod h1:WCPBHsOXfBVnivScjs2ypRfimjEW0qPVLGgJkZlrIOA= 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/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -34,89 +38,86 @@ github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcP github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 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/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/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= 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/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/aws/aws-sdk-go v1.50.0 h1:HBtrLeO+QyDKnc3t1+5DR1RxodOHCGr8ZcrHudpv7jI= -github.com/aws/aws-sdk-go v1.50.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk= -github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= -github.com/aws/aws-sdk-go-v2/config v1.26.2 h1:+RWLEIWQIGgrz2pBPAUoGgNGs1TOyF4Hml7hCnYj2jc= -github.com/aws/aws-sdk-go-v2/config v1.26.2/go.mod h1:l6xqvUxt0Oj7PI/SUXYLNyZ9T/yBPn3YTQcJLLOdtR8= -github.com/aws/aws-sdk-go-v2/credentials v1.16.13 h1:WLABQ4Cp4vXtXfOWOS3MEZKr6AAYUpMczLhgKtAjQ/8= -github.com/aws/aws-sdk-go-v2/credentials v1.16.13/go.mod h1:Qg6x82FXwW0sJHzYruxGiuApNo31UEtJvXVSZAXeWiw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.9 h1:5zA8qVCXMPGt6YneFnll5B157SfdK2SewU85PH9/yM0= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.9/go.mod h1:t4gy210hPxkbtYM8xOzrWdxVq1PyekR76OOKXy3s0Vs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM= -github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7 h1:o0ASbVwUAIrfp/WcCac+6jioZt4Hd8k/1X8u7GJ/QeM= -github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 h1:HJeiuZ2fldpd0WqngyMR6KW7ofkXNLyOaHwEIGm39Cs= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.6/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +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.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= +github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw= +github.com/aws/aws-sdk-go-v2/config v1.27.31 h1:kxBoRsjhT3pq0cKthgj6RU6bXTm/2SgdoUMyrVw0rAI= +github.com/aws/aws-sdk-go-v2/config v1.27.31/go.mod h1:z04nZdSWFPaDwK3DdJOG2r+scLQzMYuJeW0CujEm9FM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q= +github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.16 h1:1FWqcOnvnO0lRsv0kLACwwQquoZIoS5tD0MtfoNdnkk= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.16/go.mod h1:+E8OuB446P/5Swajo40TqenLMzm6aYDEEz6FZDn/u1E= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 h1:mimdLQkIX1zr8GIPY1ZtALdBQGxcASiBd2MOp8m/dMc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16/go.mod h1:YHk6owoSwrIsok+cAH9PENCOGoH5PU2EllX4vLtSrsY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 h1:GckUnpm4EJOAio1c8o25a+b3lVfwVzC9gnSBqiiNmZM= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18/go.mod h1:Br6+bxfG33Dk3ynmkhsW2Z/t9D4+lRqdLDNCKi85w0U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 h1:jg16PhLPUiHIj8zYIW6bqzeQSuHVEiWnGA0Brz5Xv2I= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16/go.mod h1:Uyk1zE1VVdsHSU7096h/rwnXDzOzYQVl+FNPhPw7ShY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.61.0 h1:Wb544Wh+xfSXqJ/j3R4aX9wrKUoZsJNmilBYZb3mKQ4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.61.0/go.mod h1:BSPI0EfnYUuNHPS0uqIo5VrRwzie+Fp+YhQOUs16sKI= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= +github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= 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/bep/simplecobra v0.4.0 h1:ufX/6WcOtEVJdCd7hsztTWURlZkOaWYOD+zCqrM8qUE= github.com/bep/simplecobra v0.4.0/go.mod h1:evSM6iQqRwqpV7W4H4DlYFfe9mZ0x6Hj5GEOnIV7dI4= -github.com/carlmjohnson/requests v0.23.5 h1:NPANcAofwwSuC6SIMwlgmHry2V3pLrSqRiSBKYbNHHA= -github.com/carlmjohnson/requests v0.23.5/go.mod h1:zG9P28thdRnN61aD7iECFhH5iGGKX2jIjKQD9kqYH+o= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso= +github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU= +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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/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/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.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= -github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -126,12 +127,12 @@ 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/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= -github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +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/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +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.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -151,33 +152,33 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-openapi/analysis v0.22.1 h1:nBWSKtx/OQr6FADwy0AYDZFdT05Abv5eYWECByHEQMg= -github.com/go-openapi/analysis v0.22.1/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= -github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= -github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/loads v0.21.5 h1:jDzF4dSoHw6ZFADCGltDb2lE4F6De7aWSpe+IcsRzT0= -github.com/go-openapi/loads v0.21.5/go.mod h1:PxTsnFBoBe+z89riT+wYt3prmSBP6GDAQh2l9H1Flz8= -github.com/go-openapi/runtime v0.26.2 h1:elWyB9MacRzvIVgAZCBJmqTi7hBzU0hlKD4IvfX0Zl0= -github.com/go-openapi/runtime v0.26.2/go.mod h1:O034jyRZ557uJKzngbMDJXkcKJVzXJiymdSfgejrcRw= -github.com/go-openapi/spec v0.20.13 h1:XJDIN+dLH6vqXgafnl5SUIMnzaChQ6QTo0/UPMbkIaE= -github.com/go-openapi/spec v0.20.13/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= -github.com/go-openapi/strfmt v0.22.0 h1:Ew9PnEYc246TwrEspvBdDHS4BVKXy/AOVsfqGDgAcaI= -github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= -github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8= -github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= -github.com/go-openapi/validate v0.22.6 h1:+NhuwcEYpWdO5Nm4bmvhGLW0rt1Fcc532Mu3wpypXfo= -github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XHwcOY+BMxKMyM= +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= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +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.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +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.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.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +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.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.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= 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= @@ -196,11 +197,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W 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.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/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -208,63 +206,56 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw 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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/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.9/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/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= -github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= +github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= +github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 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/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= -github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM= -github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= -github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= -github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0= +github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es= +github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/grafana/dashboard-linter v0.0.0-20231114210226-c458893a5731 h1:DfFbnpd1251Pe5vR/J/CBXsCJJM6N0nt+odU/8EPwwQ= -github.com/grafana/dashboard-linter v0.0.0-20231114210226-c458893a5731/go.mod h1:nciNdOgPMsTHwNUpYDfTqi8lRfZVtSZxXjtXfeXlQds= -github.com/grafana/grafana-openapi-client-go v0.0.0-20231219151618-11c46332acad h1:OuTuIuc+D5JwvNOQGbReML1UyxcMP34qWYGpA44IqLY= -github.com/grafana/grafana-openapi-client-go v0.0.0-20231219151618-11c46332acad/go.mod h1:Rvw2Gi7zbybXpAOKRdRhN28cSxUQTSgHvwHkBb1SyLo= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grafana/dashboard-linter v0.0.0-20240808111517-eb2bc3ba25e3 h1:/tTPH3D/NBDnRf250HEzaWtaTl1Mv7XJop0Yozj2GmU= +github.com/grafana/dashboard-linter v0.0.0-20240808111517-eb2bc3ba25e3/go.mod h1:oqkuR/13iWWt/23IXYqq6qTj448D1eiuLr1U1RD7D8o= +github.com/grafana/grafana-openapi-client-go v0.0.0-20240826142251-d1c93bae4198 h1:JEoUdaKnBdZ57YsWiDeAERYu52W4c7g7eAAxY2PpWl8= +github.com/grafana/grafana-openapi-client-go v0.0.0-20240826142251-d1c93bae4198/go.mod h1:hiZnMmXc9KXNUlvkV2BKFsiWuIFF/fF4wGgYWEjBitI= +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/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= 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/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc= -github.com/jedib0t/go-pretty/v6 v6.4.9/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= +github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU= +github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 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/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -273,20 +264,18 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lmittmann/tint v1.0.3 h1:W5PHeA2D8bBJVvabNfQD/XW9HPLZK1XoPZH0cq8NouQ= -github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= -github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw= +github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 h1:5RK988zAqB3/AN3opGfRpoQgAVqr6/A5+qRTi67VUZY= +github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= 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.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -298,145 +287,134 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd 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.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -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/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +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/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= 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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +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/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -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/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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= -github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +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.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= +github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.50.0 h1:gf+SN6jtbsZ70KkIGg7S3LuB4kHyUfatZLCGwZ1/aec= -github.com/prometheus/prometheus v0.50.0/go.mod h1:FvE8dtQ1Ww63IlyKBn1V4s+zMwF9kHkVNkQBR1pM4CU= +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/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= +github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -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/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= 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/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= -github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= -github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= +github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +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.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/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +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/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/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.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.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/testcontainers/testcontainers-go v0.26.0 h1:uqcYdoOHBy1ca7gKODfBd9uTHVK3a7UL848z09MVZ0c= -github.com/testcontainers/testcontainers-go v0.26.0/go.mod h1:ICriE9bLX5CLxL9OFQ2N+2N+f+803LNJ1utJb1+Inx0= +github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= +github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= -github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= -github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= +github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= -github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= -github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -444,63 +422,65 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeitlinger/conflate v0.0.0-20230622100834-279724abda8c h1:PtECnCzGLw8MuQ0tmPRaN5c95ZfNTFZOobvgC6A83zk= github.com/zeitlinger/conflate v0.0.0-20230622100834-279724abda8c/go.mod h1:KsJBt1tGR0Q7u+3T7CLN+zITAI06GiXVi/cgP9Xrpb8= -go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= -go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= +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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 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= -gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= -gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= +gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= +gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -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/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= 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/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/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.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/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= @@ -511,20 +491,25 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-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.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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -535,7 +520,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w 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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -543,69 +527,71 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc 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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/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.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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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-20190422233926-fe54fb35175b/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/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/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 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= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE= -google.golang.org/api v0.167.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= +google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= 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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 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 v0.0.0-20240221002015-b0ce06bbee7c h1:Zmyn5CV/jxzKnF+3d+xzbomACPwLQqVpLTpyXN5uTaQ= -google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed h1:4C4dbrVFtfIp3GXJdMX1Sj25mahfn5DywOo65/2ISQ8= +google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:ICjniACoWvcDz8c8bOsHVKuuSGDJy1z5M4G0DM3HzTc= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= 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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -615,10 +601,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 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.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/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= @@ -626,22 +610,21 @@ 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= -gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +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= -k8s.io/apimachinery v0.28.6 h1:RsTeR4z6S07srPg6XYrwXpTJVMXsjPXn0ODakMytSW0= -k8s.io/apimachinery v0.28.6/go.mod h1:QFNX/kCl/EMT2WTSz8k4WLCv2XnkOLMaL8GAVRMdpsA= -k8s.io/client-go v0.28.6 h1:Gge6ziyIdafRchfoBKcpaARuz7jfrK1R1azuwORIsQI= -k8s.io/client-go v0.28.6/go.mod h1:+nu0Yp21Oeo/cBCsprNVXB2BfJTV51lFfe5tXl2rUL8= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/internal/api/extended.go b/internal/api/extended.go index 23c57ed4..10c8f883 100644 --- a/internal/api/extended.go +++ b/internal/api/extended.go @@ -11,12 +11,14 @@ import ( type ExtendedApi struct { grafanaCfg *config.GrafanaConfig + debug bool } func NewExtendedApi() *ExtendedApi { - cfg := config.Config().GetDefaultGrafanaConfig() + cfg := config.Config() o := ExtendedApi{ - grafanaCfg: cfg, + grafanaCfg: cfg.GetDefaultGrafanaConfig(), + debug: cfg.IsApiDebug(), } return &o } diff --git a/internal/api/orgs.go b/internal/api/orgs.go index cda51bf9..f6ada8e8 100644 --- a/internal/api/orgs.go +++ b/internal/api/orgs.go @@ -6,6 +6,7 @@ import ( "github.com/avast/retry-go" "github.com/esnet/gdg/internal/config" "github.com/grafana/grafana-openapi-client-go/models" + "log" "log/slog" "net/http" "time" @@ -16,11 +17,16 @@ import ( func (extended *ExtendedApi) GetConfiguredOrgId(orgName string) (int64, error) { var result []*models.UserOrgDTO fetch := func() error { - return extended.getRequestBuilder(). + req := extended.getRequestBuilder(). Path("api/user/orgs"). ToJSON(&result). - Method(http.MethodGet). - Fetch(context.Background()) + Method(http.MethodGet) + + if extended.debug { + log.Printf("%v", req) + } + + return req.Fetch(context.Background()) } /* There's something goofy here. This seems to fail sporadically in grafana if we keep swapping orgs too fast. diff --git a/internal/config/config_loader.go b/internal/config/config_loader.go index 5f46306d..4a5ac574 100644 --- a/internal/config/config_loader.go +++ b/internal/config/config_loader.go @@ -176,6 +176,14 @@ func (s *Configuration) IsDebug() bool { return false } +// IsApiDebug returns true if debug mode is enabled for APIs +func (s *Configuration) IsApiDebug() bool { + if val := s.GetViperConfig(ViperGdgConfig); val != nil { + return val.GetBool("global.api_debug") + } + return false +} + // IgnoreSSL returns true if SSL errors should be ignored func (s *Configuration) IgnoreSSL() bool { return s.GetViperConfig(ViperGdgConfig).GetBool("global.ignore_ssl_errors") diff --git a/internal/config/config_loader_test.go b/internal/config/config_loader_test.go index dbd8fa06..93b9c9ff 100644 --- a/internal/config/config_loader_test.go +++ b/internal/config/config_loader_test.go @@ -3,6 +3,8 @@ package config_test import ( "fmt" "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/pkg/test_tooling/common" + "github.com/esnet/gdg/pkg/test_tooling/path" "github.com/grafana/grafana-openapi-client-go/models" "golang.org/x/exp/slices" "log/slog" @@ -15,17 +17,10 @@ import ( ) func DuplicateConfig(t *testing.T) string { - dir, _ := os.Getwd() - var err error - //Fix test path - if strings.Contains(dir, "config") { - err = os.Chdir("../..") - assert.Nil(t, err, "Failed to change to base directory ") - } - - err = os.Setenv("GDG_CONTEXT_NAME", "production") + assert.NoError(t, path.FixTestDir("config", "../..")) + err := os.Setenv("GDG_CONTEXT_NAME", "production") assert.Nil(t, err, "Failed to set override GDG context name via ENV") - data, err := os.ReadFile("config/testing.yml") + data, err := os.ReadFile("config/" + common.DefaultTestConfig) assert.Nil(t, err, "Failed to read test configuration file") destination := os.TempDir() cfgFile := fmt.Sprintf("%s/config.yml", destination) @@ -48,7 +43,7 @@ func TestSetup(t *testing.T) { } os.Setenv("GDG_CONTEXT_NAME", "qa") - config.InitGdgConfig("testing.yml", "") + config.InitGdgConfig(common.DefaultTestConfig, "") conf := config.Config().GetViperConfig(config.ViperGdgConfig) slog.Info(conf.ConfigFileUsed()) @@ -71,7 +66,7 @@ func TestWatchedFoldersConfig(t *testing.T) { } os.Setenv("GDG_CONTEXT_NAME", "qa") - config.InitGdgConfig("testing.yml", "") + config.InitGdgConfig(common.DefaultTestConfig, "") conf := config.Config().GetViperConfig(config.ViperGdgConfig) slog.Info(conf.ConfigFileUsed()) @@ -116,7 +111,7 @@ func TestSetupDifferentPath(t *testing.T) { func TestConfigEnv(t *testing.T) { os.Setenv("GDG_CONTEXT_NAME", "testing") os.Setenv("GDG_CONTEXTS__TESTING__URL", "www.google.com") - config.InitGdgConfig("testing.yml", "") + config.InitGdgConfig(common.DefaultTestConfig, "") conf := config.Config().GetViperConfig(config.ViperGdgConfig) context := conf.GetString("context_name") assert.Equal(t, context, "testing") @@ -126,7 +121,7 @@ func TestConfigEnv(t *testing.T) { assert.Equal(t, grafanaConfig.URL, url) os.Setenv("GDG_CONTEXT_NAME", "production") os.Setenv("GDG_CONTEXTS__PRODUCTION__URL", "grafana.com") - config.InitGdgConfig("testing.yml", "") + config.InitGdgConfig(common.DefaultTestConfig, "") conf = config.Config().GetViperConfig(config.ViperGdgConfig) url = conf.GetString("contexts.production.url") assert.Equal(t, url, "grafana.com") diff --git a/internal/config/config_model.go b/internal/config/config_model.go index 6c5084f0..0b5c5bee 100644 --- a/internal/config/config_model.go +++ b/internal/config/config_model.go @@ -174,8 +174,3 @@ func (s *GrafanaConfig) GetCredentials(dataSourceName models.AddDataSourceComman return nil, fmt.Errorf("no datasource credentials found for '%s', falling back on default", dataSourceName.Name) } - -// IsEnterprise Returns true when enterprise is enabled -func (s *GrafanaConfig) IsEnterprise() bool { - return s.EnterpriseSupport -} diff --git a/internal/config/globals.go b/internal/config/globals.go index faffda2b..836d4e94 100644 --- a/internal/config/globals.go +++ b/internal/config/globals.go @@ -8,6 +8,7 @@ import ( // AppGlobals is the global configuration for the application type AppGlobals struct { Debug bool `mapstructure:"debug" yaml:"debug"` + ApiDebug bool `mapstructure:"api_debug" yaml:"api_debug"` IgnoreSSLErrors bool `mapstructure:"ignore_ssl_errors" yaml:"ignore_ssl_errors"` RetryCount int `mapstructure:"retry_count" yaml:"retry_count"` RetryDelay string `mapstructure:"retry_delay" yaml:"retry_delay"` diff --git a/internal/config/grafana_config.go b/internal/config/grafana_config.go index 4b8b5550..23bf1ed0 100644 --- a/internal/config/grafana_config.go +++ b/internal/config/grafana_config.go @@ -4,7 +4,6 @@ package config type GrafanaConfig struct { Storage string `mapstructure:"storage" yaml:"storage"` grafanaAdminEnabled bool `mapstructure:"-" yaml:"-"` - EnterpriseSupport bool `mapstructure:"enterprise_support" yaml:"enterprise_support"` URL string `mapstructure:"url" yaml:"url"` APIToken string `mapstructure:"token" yaml:"token"` UserName string `mapstructure:"user_name" yaml:"user_name"` @@ -40,7 +39,11 @@ func (s *GrafanaConfig) GetOrganizationName() string { if s.OrganizationName != "" { return s.OrganizationName } - return DefaultOrganizationName + if s.IsBasicAuth() { + return DefaultOrganizationName + } + return "unknown" + } // SetGrafanaAdmin sets true if user has admin permissions diff --git a/internal/service/common.go b/internal/service/common.go index dc0071fd..632dec86 100644 --- a/internal/service/common.go +++ b/internal/service/common.go @@ -14,7 +14,6 @@ import ( var ( DefaultFolderName = "General" - DefaultFolderId = int64(0) searchTypeDashboard = "dash-db" searchTypeFolder = "dash-folder" ) @@ -59,10 +58,10 @@ func BuildResourceFolder(folderName string, resourceType config.ResourceType) st if resourceType == config.DashboardResource && folderName == "" { folderName = DefaultFolderName } - strSeperator := string(os.PathSeparator) + strSeparator := string(os.PathSeparator) - if strings.Contains(folderName, strSeperator) { - folderName = strings.ReplaceAll(folderName, strSeperator, fmt.Sprintf("//%s", strSeperator)) + if strings.Contains(folderName, strSeparator) { + folderName = strings.ReplaceAll(folderName, strSeparator, fmt.Sprintf("//%s", strSeparator)) } v := fmt.Sprintf("%s/%s", config.Config().GetDefaultGrafanaConfig().GetPath(resourceType), folderName) tools.CreateDestinationPath(v) diff --git a/internal/service/common_test.go b/internal/service/common_test.go index 85ecd0c1..7caa6e79 100644 --- a/internal/service/common_test.go +++ b/internal/service/common_test.go @@ -1,60 +1,68 @@ package service import ( + "os" + "testing" + "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/pkg/test_tooling/common" + "github.com/esnet/gdg/pkg/test_tooling/path" "github.com/gosimple/slug" "github.com/stretchr/testify/assert" - "os" - "strings" - "testing" ) -func TestRelativePathLogin(t *testing.T) { - cwd, err := os.Getwd() +func fixEnvironment(t *testing.T) { + assert.NoError(t, path.FixTestDir("service", "../..")) + err := os.Setenv("GDG_CONTEXT_NAME", "qa") assert.Nil(t, err) - if strings.Contains(cwd, "service") { - os.Chdir("../..") - } - os.Setenv("GDG_CONTEXTS__TESTING__URL", "http://localhost:3000/grafana/") - config.InitGdgConfig("config/testing.yml", "'") - defer os.Unsetenv("GDG_CONTEXTS__TESTING__URL") + config.InitGdgConfig(common.DefaultTestConfig, "'") +} + +func TestRelativePathLogin(t *testing.T) { + envKey := "GDG_CONTEXTS__QA__URL" + assert.NoError(t, os.Setenv(envKey, "http://localhost:3000/grafana/")) + fixEnvironment(t) + config.InitGdgConfig(common.DefaultTestConfig, "'") + defer assert.NoError(t, os.Unsetenv(envKey)) svc := NewApiService("dummy") _, cfg := svc.(*DashNGoImpl).getNewClient() assert.Equal(t, cfg.Host, "localhost:3000") assert.Equal(t, cfg.BasePath, "/grafana/api") - } // Validates the paths for the various entity types using the common // code used to create folders and generate paths. func TestSlug(t *testing.T) { + fixEnvironment(t) result := GetSlug("thisTestMoo") assert.Equal(t, "thistestmoo", result) - //This + // This result = GetSlug("This Test Moo") assert.Equal(t, "this-test-moo", result) } func TestUserPath(t *testing.T) { - err := os.Setenv("GDG_CONTEXT_NAME", "qa") - assert.Nil(t, err) - config.InitGdgConfig("testing.yml", "'") + fixEnvironment(t) path := BuildResourceFolder("", config.UserResource) assert.Equal(t, "test/data/users/", path) } + func TestBuildDashboardPath(t *testing.T) { + fixEnvironment(t) result := BuildResourceFolder("General", config.DashboardResource) assert.Equal(t, "test/data/org_your-org/dashboards/General", result) } func TestBuildFolderSourcePath(t *testing.T) { + fixEnvironment(t) result := buildResourcePath(slug.Make("Some Folder"), config.FolderResource) assert.Equal(t, "test/data/org_your-org/folders/some-folder.json", result) - } func TestBuildDataSourcePath(t *testing.T) { + fixEnvironment(t) + result := buildResourcePath(slug.Make("My DS"), config.ConnectionResource) assert.Equal(t, "test/data/org_your-org/connections/my-ds.json", result) } diff --git a/internal/service/connection_permissions.go b/internal/service/connection_permissions.go index 5eea9924..e547fcd3 100644 --- a/internal/service/connection_permissions.go +++ b/internal/service/connection_permissions.go @@ -3,6 +3,9 @@ package service import ( "encoding/json" "fmt" + "github.com/esnet/gdg/internal/tools" + "github.com/esnet/gdg/internal/types" + "github.com/grafana/grafana-openapi-client-go/client/access_control" "log" "log/slog" "path/filepath" @@ -10,27 +13,47 @@ import ( "github.com/esnet/gdg/internal/config" "github.com/esnet/gdg/internal/service/filters" - "github.com/esnet/gdg/internal/tools" "github.com/gosimple/slug" - "github.com/grafana/grafana-openapi-client-go/client/datasource_permissions" "github.com/grafana/grafana-openapi-client-go/models" ) +type PermissionType string + +const ( + ConnectionUserPermission PermissionType = "UserPermission" + ConnectionTeamPermission PermissionType = "TeamPermission" + ConnectionRolePermission PermissionType = "RolePermission" + connectionResourceType string = "datasources" +) + +const connectionPermissionMinVersion = "v10.2.3" + // ListConnectionPermissions lists all connection permission matching the given filter -func (s *DashNGoImpl) ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO { - if !s.grafanaConf.IsEnterprise() { +func (s *DashNGoImpl) ListConnectionPermissions(filter filters.Filter) []types.ConnectionPermissionItem { + if !s.IsEnterprise() { log.Fatal("Requires Enterprise to be enabled. Please check your GDG configuration and try again") + } else if !tools.ValidateMinimumVersion(connectionPermissionMinVersion, s) { + slog.Warn("Permission with connection is broken prior to 10.2.3. GDG won't support a prior version. Listing is allowed, but all other operations won't work.", + slog.Any("Your Grafana Version", "v"+s.GetServerInfo()["Version"].(string))) } - result := make(map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) + result := make([]types.ConnectionPermissionItem, 0) connections := s.ListConnections(filter) for ndx, connection := range connections { - permission, err := s.getConnectionPermission(connection.ID) + + permission, err := s.getConnectionPermission(connection.UID) if err != nil { - slog.Error("unable to retrieve connection permissions for ID", "id", connection.ID) + slog.Error("unable to retrieve connection permissions for ID.", + slog.Any("err", err), + slog.Any("uid", connection.UID), + slog.Any("connection_name", connection.Name), + ) continue } - result[&connections[ndx]] = permission.GetPayload() - + entry := types.ConnectionPermissionItem{ + Connection: &connections[ndx], + Permissions: permission.GetPayload(), + } + result = append(result, entry) } return result @@ -44,15 +67,18 @@ func (s *DashNGoImpl) DownloadConnectionPermissions(filter filters.Filter) []str err error dataFiles []string ) + if !tools.ValidateMinimumVersion(connectionPermissionMinVersion, s) { + log.Fatalf("Permission with connection is broken prior to 10.2.3. GDG won't support a prior version. Listing is allowed, but all other operations won't work. Your Grafana version is: v%s", s.GetServerInfo()["Version"].(string)) + } currentPermissions := s.ListConnectionPermissions(filter) - for connection, permission := range currentPermissions { - if dsPacked, err = json.MarshalIndent(permission, "", " "); err != nil { - slog.Error("unable to marshall json ", "err", err.Error(), "connectionName", connection.Name) + for _, connection := range currentPermissions { + if dsPacked, err = json.MarshalIndent(connection, "", " "); err != nil { + slog.Error("unable to marshall json ", "err", err.Error(), "connectionName", connection.Connection.Name) continue } - dsPath := buildResourcePath(slug.Make(connection.Name), config.ConnectionPermissionResource) + dsPath := buildResourcePath(slug.Make(connection.Connection.Name), config.ConnectionPermissionResource) if err = s.storage.WriteFile(dsPath, dsPacked); err != nil { - slog.Error("unable to write file. ", "filename", slug.Make(connection.Name), "error", err.Error()) + slog.Error("unable to write file. ", "filename", slug.Make(connection.Connection.Name), "error", err.Error()) } else { dataFiles = append(dataFiles, dsPath) } @@ -62,13 +88,20 @@ func (s *DashNGoImpl) DownloadConnectionPermissions(filter filters.Filter) []str // UploadConnectionPermissions upload connection permissions func (s *DashNGoImpl) UploadConnectionPermissions(filter filters.Filter) []string { + if !tools.ValidateMinimumVersion(connectionPermissionMinVersion, s) { + log.Fatalf("Permission with connection is broken prior to 10.2.3. GDG won't support a prior version. Listing is allowed, but all other operations won't work. Your Grafana version is: v%s", s.GetServerInfo()["Version"].(string)) + } + if !s.IsEnterprise() { + log.Fatal("Requires Enterprise to be enabled. Please check your GDG configuration and try again") + } + //if !tools.ValidateMinimumVersion("11.0.0", s) { + // log.Fatal("Behavior prior to version 11.0.0 is broken. ") + // + //} var ( rawFolder []byte dataFiles []string ) - if !s.grafanaConf.IsEnterprise() { - log.Fatal("Requires Enterprise to be enabled. Please check your GDG configuration and try again") - } filesInDir, err := s.storage.FindAllFiles(config.Config().GetDefaultGrafanaConfig().GetPath(config.ConnectionPermissionResource), false) if err != nil { @@ -87,45 +120,45 @@ func (s *DashNGoImpl) UploadConnectionPermissions(filter filters.Filter) []strin } } - newEntries := new(models.DataSourcePermissionsDTO) + newEntries := new(types.ConnectionPermissionItem) err = json.Unmarshal(rawFolder, &newEntries) if err != nil { slog.Warn("Failed to Decode payload for file", "filename", fileLocation) continue } // Get current permissions - permissions, err := s.getConnectionPermission(newEntries.DatasourceID) + permissions, err := s.getConnectionPermission(newEntries.Connection.UID) if err != nil { slog.Error("connection permission could not be retrieved, cannot update permissions") continue } - success := true + var removePermissionError error // Delete datasource Permissions - for _, p := range permissions.GetPayload().Permissions { - success = s.deleteConnectionPermission(p.ID, newEntries.DatasourceID) + for _, p := range permissions.GetPayload() { + err := s.updatedConnectionPermission(newEntries.Connection, p, "") + if err != nil { + removePermissionError = err + } + } - if !success { + if removePermissionError != nil { slog.Error("Failed to delete previous data, cannot update permissions") continue } - for _, entry := range newEntries.Permissions { - p := datasource_permissions.NewAddPermissionParams() - p.SetUserID(tools.PtrOf(entry.UserID)) - p.SetDatasourceID(fmt.Sprintf("%d", entry.DatasourceID)) - p.SetTeamID(tools.PtrOf(entry.TeamID)) - p.SetPermission(tools.PtrOf(int64(entry.Permission))) - if entry.BuiltInRole != "" { - p.SetBuiltinRole(tools.PtrOf(entry.BuiltInRole)) - } - _, err = s.GetClient().DatasourcePermissions.AddPermission(p) + success := true + for _, permission := range newEntries.Permissions { + err = s.updatedConnectionPermission(newEntries.Connection, permission, permission.Permission) if err != nil { - slog.Error("Failed to update folder permissions") - } else { - dataFiles = append(dataFiles, fileLocation) + slog.Error("Failed to update connection permissions", slog.Any("userId", permission.UserLogin), slog.Any("team", permission.Team), slog.Any("role", permission.BuiltInRole), slog.Any("permission", permission.Permission)) + success = false } + + } + if success { + dataFiles = append(dataFiles, fileLocation) } } @@ -135,37 +168,108 @@ func (s *DashNGoImpl) UploadConnectionPermissions(filter filters.Filter) []strin // DeleteAllConnectionPermissions clear all non-default permissions from all connections func (s *DashNGoImpl) DeleteAllConnectionPermissions(filter filters.Filter) []string { + if !tools.ValidateMinimumVersion(connectionPermissionMinVersion, s) { + log.Fatalf("Permission with connection is broken prior to 10.2.3. GDG won't support a prior version. Listing is allowed, but all other operations won't work. Your Grafana version is: v%s", s.GetServerInfo()["Version"].(string)) + } dataSources := make([]string, 0) connectionPermissions := s.ListConnectionPermissions(filter) - for key, connection := range connectionPermissions { + for _, conn := range connectionPermissions { success := true - for _, p := range connection.Permissions { - res := s.deleteConnectionPermission(p.ID, connection.DatasourceID) - if !res { + for _, p := range conn.Permissions { + deleteConnectionErr := s.updatedConnectionPermission(conn.Connection, p, "") + if deleteConnectionErr != nil { success = false } } if success { - dataSources = append(dataSources, key.Name) + dataSources = append(dataSources, conn.Connection.Name) } } return dataSources } -// deleteConnectionPermission delete a given permission associated with a given datasourceId -func (s *DashNGoImpl) deleteConnectionPermission(permissionId int64, datasourceId int64) bool { - permissionIdStr := fmt.Sprintf("%d", permissionId) - connectionId := fmt.Sprintf("%d", datasourceId) - resp, err := s.GetClient().DatasourcePermissions.DeletePermissions(permissionIdStr, connectionId) - if err != nil { - return false +func getPermissionType(perm models.ResourcePermissionDTO) PermissionType { + if perm.Team != "" { + return ConnectionTeamPermission + } else if perm.UserLogin != "" { + return ConnectionUserPermission + } + + return ConnectionRolePermission + +} + +// updatedConnectionPermission a given permission associated with a given resource. If permission is empty string, it will be removed, otherwise it will be added. +func (s *DashNGoImpl) updatedConnectionPermission(key *models.DataSourceListItemDTO, perm *models.ResourcePermissionDTO, permission string) error { + action := "Added" + if permission == "" { + action = "Removed" + } + permissionIdStr := fmt.Sprintf("%d", perm.ID) + connectionId := key.UID + switch permType := getPermissionType(*perm); permType { + case ConnectionRolePermission: + if perm.Permission == "Admin" { + slog.Info("Skipping modifications to admin role permission") + return nil + } + //update User Role + //POST /api/access-control/datasources/:uid/builtInRoles/:builtinRoleName + p := access_control.NewSetResourcePermissionsForBuiltInRoleParams() + p.BuiltInRole = perm.BuiltInRole + p.Resource = connectionResourceType + p.ResourceID = key.UID + p.Body = &models.SetPermissionCommand{Permission: permission} + r, err := s.GetClient().AccessControl.SetResourcePermissionsForBuiltInRole(p) + if r != nil { + slog.Debug(action+" access for builtInRole", slog.String("role", perm.BuiltInRole), slog.String("permissionID", permissionIdStr), slog.String("message", r.GetPayload().Message)) + } + if err != nil { + return err + } + case ConnectionUserPermission: + if perm.UserLogin == "admin" && perm.UserID == 1 { + slog.Info("Skipping modifications to admin user permission") + return nil + } + //POST /api/access-control/datasources/:uid/users/:id + p := access_control.NewSetResourcePermissionsForUserParams() + p.UserID = perm.UserID + p.Body = &models.SetPermissionCommand{Permission: permission} + p.Resource = connectionResourceType + p.ResourceID = connectionId + r, err := s.GetClient().AccessControl.SetResourcePermissionsForUser(p) + if r != nil { + slog.Debug(action+" access for user", slog.String("user", perm.UserLogin), slog.String("permissionID", permissionIdStr), slog.String("message", r.GetPayload().Message)) + } + if err != nil { + return err + } + case ConnectionTeamPermission: + //delete Team + //POST /api/access-control/datasources/:uid/builtInRoles/:builtinRoleName + p := access_control.NewSetResourcePermissionsForTeamParams() + p.TeamID = perm.TeamID + p.Resource = connectionResourceType + p.ResourceID = connectionId + p.Body = &models.SetPermissionCommand{Permission: permission} + r, err := s.GetClient().AccessControl.SetResourcePermissionsForTeam(p) + if r != nil { + slog.Debug(action+" access for team", slog.String("team", perm.Team), slog.String("permissionID", permissionIdStr), slog.String("message", r.GetPayload().Message)) + } + if err != nil { + return err + } + default: + slog.Warn("permission type is not supported", slog.Any("permissionType", permType)) + return fmt.Errorf("permission type %s is not supported", permType) } - slog.Debug("permission has been removed associated with datasource %d: %s", "permissionId", permissionId, "datasourceId", datasourceId, "response", resp.GetPayload().Message) - return true + return nil + } // getConnectionPermission Get all permissions for a given connection -func (s *DashNGoImpl) getConnectionPermission(id int64) (*datasource_permissions.GetAllPermissionsOK, error) { - return s.GetClient().DatasourcePermissions.GetAllPermissions(fmt.Sprintf("%d", id)) +func (s *DashNGoImpl) getConnectionPermission(uid string) (*access_control.GetResourcePermissionsOK, error) { + return s.GetClient().AccessControl.GetResourcePermissions(uid, connectionResourceType) } diff --git a/internal/service/contracts.go b/internal/service/contracts.go index 5a937c49..dc6d546e 100644 --- a/internal/service/contracts.go +++ b/internal/service/contracts.go @@ -3,6 +3,7 @@ package service import ( "github.com/esnet/gdg/internal/service/filters" "github.com/esnet/gdg/internal/service/types" + customModels "github.com/esnet/gdg/internal/types" gdgType "github.com/esnet/gdg/internal/types" "github.com/grafana/grafana-openapi-client-go/models" ) @@ -23,6 +24,11 @@ type GrafanaService interface { AuthenticationApi //MetaData ServerInfoApi + LicenseApi +} + +type LicenseApi interface { + IsEnterprise() bool } // ConnectionsApi Contract definition @@ -36,7 +42,7 @@ type ConnectionsApi interface { type ConnectionPermissions interface { // Permissions Enterprise only - ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO + ListConnectionPermissions(filter filters.Filter) []customModels.ConnectionPermissionItem DownloadConnectionPermissions(filter filters.Filter) []string UploadConnectionPermissions(filter filters.Filter) []string DeleteAllConnectionPermissions(filter filters.Filter) []string diff --git a/internal/service/dashboards.go b/internal/service/dashboards.go index ab51891e..b423fd86 100644 --- a/internal/service/dashboards.go +++ b/internal/service/dashboards.go @@ -261,7 +261,7 @@ func (s *DashNGoImpl) ListDashboards(filterReq filters.Filter) []*models.Hit { link.FolderTitle = DefaultFolderName } - if validFolder && validUid { + if validUid { deduplicatedLinks[link.ID] = boardLinks[ndx] } } @@ -310,15 +310,15 @@ func (s *DashNGoImpl) DownloadDashboards(filter filters.Filter) []string { } // createFolder Creates a new folder with the given name. -func (s *DashNGoImpl) createdFolder(folderName string) (int64, error) { +func (s *DashNGoImpl) createdFolder(folderName string) (string, error) { request := &models.CreateFolderCommand{ Title: folderName, } folder, err := s.GetClient().Folders.CreateFolder(request) if err != nil { - return 0, err + return "", err } - return folder.GetPayload().ID, nil + return folder.GetPayload().UID, nil } @@ -329,7 +329,7 @@ func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) { var ( rawBoard []byte folderName string - folderId int64 + folderUid string ) path := config.Config().GetDefaultGrafanaConfig().GetPath(config.DashboardResource) filesInDir, err := s.storage.FindAllFiles(path, true) @@ -339,7 +339,7 @@ func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) { //Delete all dashboards that match prior to import s.DeleteAllDashboards(filterReq) - folderMap := getFolderNameIDMap(s.ListFolder(NewFolderFilter())) + folderUidMap := getFolderNameUIDMap(s.ListFolder(NewFolderFilter())) // Fallback on defaults if filterReq == nil { @@ -397,7 +397,6 @@ func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) { } if folderName == "" || folderName == DefaultFolderName { - folderId = DefaultFolderId folderName = DefaultFolderName } if !slices.Contains(validFolders, folderName) && !config.Config().GetDefaultGrafanaConfig().GetFilterOverrides().IgnoreDashboardFilters { @@ -411,19 +410,20 @@ func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) { } if folderName == DefaultFolderName { - folderId = DefaultFolderId + folderUid = "" } else { - if val, ok := folderMap[folderName]; ok { - folderId = val + if val, ok := folderUidMap[folderName]; ok { + //folderId = val + folderUid = val } else { if filterReq.ValidateAll(validateMap) { id, folderErr := s.createdFolder(folderName) if folderErr != nil { log.Panic("Unable to create required folder") } else { - folderMap[folderName] = id - folderId = id + folderUidMap[folderName] = id + folderUid = id } } } @@ -439,7 +439,7 @@ func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) { //zero out ID. Can't create a new dashboard if an ID already exists. delete(data, "id") importDashReq := &models.ImportDashboardRequest{ - FolderID: folderId, + FolderUID: folderUid, Overwrite: true, Dashboard: data, } diff --git a/internal/service/folders.go b/internal/service/folders.go index 2bb53d94..545b1344 100644 --- a/internal/service/folders.go +++ b/internal/service/folders.go @@ -281,6 +281,15 @@ func getFolderNameIDMap(folders []*models.Hit) map[string]int64 { return folderMap } +// getFolderNameUIDMap helper function to build a mapping for name to folderID +func getFolderNameUIDMap(folders []*models.Hit) map[string]string { + var folderMap = make(map[string]string) + for _, folder := range folders { + folderMap[folder.Title] = folder.UID + } + return folderMap +} + // Creates a reverse look up map, where the values are the keys and the keys are the values. func reverseLookUp[T comparable, Y comparable](m map[T]Y) map[Y]T { reverse := make(map[Y]T, 0) diff --git a/internal/service/gdg_api.go b/internal/service/gdg_api.go index ef13273b..1598b813 100644 --- a/internal/service/gdg_api.go +++ b/internal/service/gdg_api.go @@ -6,6 +6,7 @@ import ( "github.com/esnet/gdg/internal/config" "github.com/spf13/viper" "log/slog" + "os" "sync" ) @@ -20,6 +21,7 @@ type DashNGoImpl struct { grafanaConf *config.GrafanaConfig configRef *viper.Viper debug bool + apiDebug bool storage Storage } @@ -34,9 +36,20 @@ func newInstance() *DashNGoImpl { obj := &DashNGoImpl{} obj.grafanaConf = config.Config().GetDefaultGrafanaConfig() obj.configRef = config.Config().GetViperConfig(config.ViperGdgConfig) - obj.Login() - obj.debug = config.Config().IsDebug() + obj.apiDebug = config.Config().IsApiDebug() + if obj.apiDebug { + err := os.Setenv("DEBUG", "1") + if err != nil { + slog.Debug("unable to set debug env value", slog.Any("err", err)) + } + } else { + err := os.Setenv("DEBUG", "0") + if err != nil { + slog.Debug("unable to set debug env value", slog.Any("err", err)) + } + } + obj.Login() configureStorage(obj) return obj diff --git a/internal/service/libraryelements.go b/internal/service/libraryelements.go index 123b2581..9f812fe2 100644 --- a/internal/service/libraryelements.go +++ b/internal/service/libraryelements.go @@ -83,7 +83,7 @@ func (s *DashNGoImpl) DownloadLibraryElements(filter filters.Filter) []string { dataFiles []string ) - folderMap := reverseLookUp(getFolderNameIDMap(s.ListFolder(nil))) + folderMap := reverseLookUp(getFolderNameUIDMap(s.ListFolder(nil))) listing = s.ListLibraryElements(filter) for _, item := range listing { if dsPacked, err = json.MarshalIndent(item, "", " "); err != nil { @@ -92,7 +92,7 @@ func (s *DashNGoImpl) DownloadLibraryElements(filter filters.Filter) []string { } folderName := DefaultFolderName - if val, ok := folderMap[item.FolderID]; ok { + if val, ok := folderMap[item.FolderUID]; ok { folderName = val } diff --git a/internal/service/license.go b/internal/service/license.go new file mode 100644 index 00000000..7164f30f --- /dev/null +++ b/internal/service/license.go @@ -0,0 +1,11 @@ +package service + +// IsEnterprise will return a valid response if the grafana version is running an enterprise version +func (s *DashNGoImpl) IsEnterprise() bool { + r, err := s.GetClient().Licensing.GetStatus() + if err != nil { + return false + } + + return r.IsSuccess() +} diff --git a/internal/service/login.go b/internal/service/login.go index b6d9b3e1..80cc6420 100644 --- a/internal/service/login.go +++ b/internal/service/login.go @@ -5,6 +5,7 @@ import ( "github.com/esnet/gdg/internal/api" "github.com/esnet/gdg/internal/config" "github.com/go-openapi/strfmt" + "github.com/grafana/grafana-openapi-client-go/models" "log" "log/slog" "net/http" @@ -17,10 +18,13 @@ import ( func (s *DashNGoImpl) Login() { var err error // Will only succeed for BasicAuth - userInfo, err := s.GetUserInfo() - // Sets state based on user permissions - if err == nil { - s.grafanaConf.SetGrafanaAdmin(userInfo.IsGrafanaAdmin) + if s.grafanaConf.IsBasicAuth() { + var userInfo *models.UserProfileDTO + userInfo, err = s.GetUserInfo() + // Sets state based on user permissions + if err == nil { + s.grafanaConf.SetGrafanaAdmin(userInfo.IsGrafanaAdmin) + } } s.extended = api.NewExtendedApi() @@ -50,21 +54,24 @@ func (s *DashNGoImpl) getNewClient(opts ...NewClientOpts) (*client.GrafanaHTTPAP Schemes: []string{u.Scheme}, NumRetries: config.Config().GetGDGConfig().GetAppGlobals().RetryCount, RetryTimeout: config.Config().GetGDGConfig().GetAppGlobals().GetRetryTimeout(), + Debug: s.apiDebug, } - if s.grafanaConf.OrganizationName != "" { - orgId, err := api.NewExtendedApi().GetConfiguredOrgId(s.grafanaConf.OrganizationName) - if err != nil { - slog.Error("unable to determine org ID, falling back", slog.Any("err", err)) - orgId = 1 + if s.grafanaConf.IsBasicAuth() { + if s.grafanaConf.OrganizationName != "" { + orgId, err := api.NewExtendedApi().GetConfiguredOrgId(s.grafanaConf.OrganizationName) + if err != nil { + slog.Error("unable to determine org ID, falling back", slog.Any("err", err)) + orgId = 1 + } + opts = append(opts, func(clientCfg *client.TransportConfig) { + clientCfg.OrgID = orgId + }) + } else { + opts = append(opts, func(clientCfg *client.TransportConfig) { + clientCfg.OrgID = config.DefaultOrganizationId + }) } - opts = append(opts, func(clientCfg *client.TransportConfig) { - clientCfg.OrgID = orgId - }) - } else { - opts = append(opts, func(clientCfg *client.TransportConfig) { - clientCfg.OrgID = config.DefaultOrganizationId - }) } for _, opt := range opts { if opt != nil { @@ -83,6 +90,7 @@ func (s *DashNGoImpl) GetClient() *client.GrafanaHTTPAPI { if s.grafanaConf.APIToken != "" { grafanaClient, _ := s.getNewClient(func(clientCfg *client.TransportConfig) { clientCfg.APIKey = s.grafanaConf.APIToken + clientCfg.Debug = s.apiDebug }) return grafanaClient } else { @@ -102,6 +110,7 @@ func (s *DashNGoImpl) GetAdminClient() *client.GrafanaHTTPAPI { func (s *DashNGoImpl) GetBasicAuthClient() *client.GrafanaHTTPAPI { grafanaClient, _ := s.getNewClient(func(clientCfg *client.TransportConfig) { clientCfg.BasicAuth = url.UserPassword(s.grafanaConf.UserName, s.grafanaConf.Password) + clientCfg.Debug = s.apiDebug }) return grafanaClient } diff --git a/internal/service/mocks/ConnectionPermissions.go b/internal/service/mocks/ConnectionPermissions.go index bf528287..4bde69a3 100644 --- a/internal/service/mocks/ConnectionPermissions.go +++ b/internal/service/mocks/ConnectionPermissions.go @@ -6,7 +6,7 @@ import ( filters "github.com/esnet/gdg/internal/service/filters" mock "github.com/stretchr/testify/mock" - models "github.com/grafana/grafana-openapi-client-go/models" + types "github.com/esnet/gdg/internal/types" ) // ConnectionPermissions is an autogenerated mock type for the ConnectionPermissions type @@ -119,19 +119,19 @@ func (_c *ConnectionPermissions_DownloadConnectionPermissions_Call) RunAndReturn } // ListConnectionPermissions provides a mock function with given fields: filter -func (_m *ConnectionPermissions) ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO { +func (_m *ConnectionPermissions) ListConnectionPermissions(filter filters.Filter) []types.ConnectionPermissionItem { ret := _m.Called(filter) if len(ret) == 0 { panic("no return value specified for ListConnectionPermissions") } - var r0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO - if rf, ok := ret.Get(0).(func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO); ok { + var r0 []types.ConnectionPermissionItem + if rf, ok := ret.Get(0).(func(filters.Filter) []types.ConnectionPermissionItem); ok { r0 = rf(filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) + r0 = ret.Get(0).([]types.ConnectionPermissionItem) } } @@ -156,12 +156,12 @@ func (_c *ConnectionPermissions_ListConnectionPermissions_Call) Run(run func(fil return _c } -func (_c *ConnectionPermissions_ListConnectionPermissions_Call) Return(_a0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *ConnectionPermissions_ListConnectionPermissions_Call { +func (_c *ConnectionPermissions_ListConnectionPermissions_Call) Return(_a0 []types.ConnectionPermissionItem) *ConnectionPermissions_ListConnectionPermissions_Call { _c.Call.Return(_a0) return _c } -func (_c *ConnectionPermissions_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *ConnectionPermissions_ListConnectionPermissions_Call { +func (_c *ConnectionPermissions_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) []types.ConnectionPermissionItem) *ConnectionPermissions_ListConnectionPermissions_Call { _c.Call.Return(run) return _c } diff --git a/internal/service/mocks/ConnectionsApi.go b/internal/service/mocks/ConnectionsApi.go index d3544b8a..3e47bf88 100644 --- a/internal/service/mocks/ConnectionsApi.go +++ b/internal/service/mocks/ConnectionsApi.go @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/grafana/grafana-openapi-client-go/models" + + types "github.com/esnet/gdg/internal/types" ) // ConnectionsApi is an autogenerated mock type for the ConnectionsApi type @@ -215,19 +217,19 @@ func (_c *ConnectionsApi_DownloadConnections_Call) RunAndReturn(run func(filters } // ListConnectionPermissions provides a mock function with given fields: filter -func (_m *ConnectionsApi) ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO { +func (_m *ConnectionsApi) ListConnectionPermissions(filter filters.Filter) []types.ConnectionPermissionItem { ret := _m.Called(filter) if len(ret) == 0 { panic("no return value specified for ListConnectionPermissions") } - var r0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO - if rf, ok := ret.Get(0).(func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO); ok { + var r0 []types.ConnectionPermissionItem + if rf, ok := ret.Get(0).(func(filters.Filter) []types.ConnectionPermissionItem); ok { r0 = rf(filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) + r0 = ret.Get(0).([]types.ConnectionPermissionItem) } } @@ -252,12 +254,12 @@ func (_c *ConnectionsApi_ListConnectionPermissions_Call) Run(run func(filter fil return _c } -func (_c *ConnectionsApi_ListConnectionPermissions_Call) Return(_a0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *ConnectionsApi_ListConnectionPermissions_Call { +func (_c *ConnectionsApi_ListConnectionPermissions_Call) Return(_a0 []types.ConnectionPermissionItem) *ConnectionsApi_ListConnectionPermissions_Call { _c.Call.Return(_a0) return _c } -func (_c *ConnectionsApi_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *ConnectionsApi_ListConnectionPermissions_Call { +func (_c *ConnectionsApi_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) []types.ConnectionPermissionItem) *ConnectionsApi_ListConnectionPermissions_Call { _c.Call.Return(run) return _c } diff --git a/internal/service/mocks/GrafanaService.go b/internal/service/mocks/GrafanaService.go index e8c5f0f9..28f9a580 100644 --- a/internal/service/mocks/GrafanaService.go +++ b/internal/service/mocks/GrafanaService.go @@ -1509,6 +1509,51 @@ func (_c *GrafanaService_InitOrganizations_Call) RunAndReturn(run func()) *Grafa return _c } +// IsEnterprise provides a mock function with given fields: +func (_m *GrafanaService) IsEnterprise() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for IsEnterprise") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// GrafanaService_IsEnterprise_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnterprise' +type GrafanaService_IsEnterprise_Call struct { + *mock.Call +} + +// IsEnterprise is a helper method to define mock.On call +func (_e *GrafanaService_Expecter) IsEnterprise() *GrafanaService_IsEnterprise_Call { + return &GrafanaService_IsEnterprise_Call{Call: _e.mock.On("IsEnterprise")} +} + +func (_c *GrafanaService_IsEnterprise_Call) Run(run func()) *GrafanaService_IsEnterprise_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GrafanaService_IsEnterprise_Call) Return(_a0 bool) *GrafanaService_IsEnterprise_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GrafanaService_IsEnterprise_Call) RunAndReturn(run func() bool) *GrafanaService_IsEnterprise_Call { + _c.Call.Return(run) + return _c +} + // LintDashboards provides a mock function with given fields: req func (_m *GrafanaService) LintDashboards(req types.LintRequest) []string { ret := _m.Called(req) @@ -1605,19 +1650,19 @@ func (_c *GrafanaService_ListAPIKeys_Call) RunAndReturn(run func() []*models.API } // ListConnectionPermissions provides a mock function with given fields: filter -func (_m *GrafanaService) ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO { +func (_m *GrafanaService) ListConnectionPermissions(filter filters.Filter) []internaltypes.ConnectionPermissionItem { ret := _m.Called(filter) if len(ret) == 0 { panic("no return value specified for ListConnectionPermissions") } - var r0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO - if rf, ok := ret.Get(0).(func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO); ok { + var r0 []internaltypes.ConnectionPermissionItem + if rf, ok := ret.Get(0).(func(filters.Filter) []internaltypes.ConnectionPermissionItem); ok { r0 = rf(filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) + r0 = ret.Get(0).([]internaltypes.ConnectionPermissionItem) } } @@ -1642,12 +1687,12 @@ func (_c *GrafanaService_ListConnectionPermissions_Call) Run(run func(filter fil return _c } -func (_c *GrafanaService_ListConnectionPermissions_Call) Return(_a0 map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *GrafanaService_ListConnectionPermissions_Call { +func (_c *GrafanaService_ListConnectionPermissions_Call) Return(_a0 []internaltypes.ConnectionPermissionItem) *GrafanaService_ListConnectionPermissions_Call { _c.Call.Return(_a0) return _c } -func (_c *GrafanaService_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO) *GrafanaService_ListConnectionPermissions_Call { +func (_c *GrafanaService_ListConnectionPermissions_Call) RunAndReturn(run func(filters.Filter) []internaltypes.ConnectionPermissionItem) *GrafanaService_ListConnectionPermissions_Call { _c.Call.Return(run) return _c } diff --git a/internal/service/mocks/LicenseApi.go b/internal/service/mocks/LicenseApi.go new file mode 100644 index 00000000..d68af936 --- /dev/null +++ b/internal/service/mocks/LicenseApi.go @@ -0,0 +1,77 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// LicenseApi is an autogenerated mock type for the LicenseApi type +type LicenseApi struct { + mock.Mock +} + +type LicenseApi_Expecter struct { + mock *mock.Mock +} + +func (_m *LicenseApi) EXPECT() *LicenseApi_Expecter { + return &LicenseApi_Expecter{mock: &_m.Mock} +} + +// IsEnterprise provides a mock function with given fields: +func (_m *LicenseApi) IsEnterprise() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for IsEnterprise") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// LicenseApi_IsEnterprise_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnterprise' +type LicenseApi_IsEnterprise_Call struct { + *mock.Call +} + +// IsEnterprise is a helper method to define mock.On call +func (_e *LicenseApi_Expecter) IsEnterprise() *LicenseApi_IsEnterprise_Call { + return &LicenseApi_IsEnterprise_Call{Call: _e.mock.On("IsEnterprise")} +} + +func (_c *LicenseApi_IsEnterprise_Call) Run(run func()) *LicenseApi_IsEnterprise_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *LicenseApi_IsEnterprise_Call) Return(_a0 bool) *LicenseApi_IsEnterprise_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *LicenseApi_IsEnterprise_Call) RunAndReturn(run func() bool) *LicenseApi_IsEnterprise_Call { + _c.Call.Return(run) + return _c +} + +// NewLicenseApi creates a new instance of LicenseApi. 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 NewLicenseApi(t interface { + mock.TestingT + Cleanup(func()) +}) *LicenseApi { + mock := &LicenseApi{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/service/organizations.go b/internal/service/organizations.go index d820dd47..4606cfec 100644 --- a/internal/service/organizations.go +++ b/internal/service/organizations.go @@ -119,11 +119,8 @@ func (s *DashNGoImpl) InitOrganizations() { } } - } else { - orgInfo = &models.OrgDetailsDTO{ - Name: s.grafanaConf.GetOrganizationName(), - } - + } else if s.grafanaConf.GetOrganizationName() != "unknown" { + slog.Warn("Tokens do no operate across multiple Organizations. Cannot verify or scope into the given org. Please be sure your token belongs to the correct organization", slog.String("configure organization", s.grafanaConf.GetOrganizationName())) } if s.grafanaConf.IsGrafanaAdmin() { diff --git a/internal/service/storage_cloud.go b/internal/service/storage_cloud.go index a8a09f4b..8acb73f1 100644 --- a/internal/service/storage_cloud.go +++ b/internal/service/storage_cloud.go @@ -4,10 +4,12 @@ import ( "context" "errors" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/s3" + transport "github.com/aws/smithy-go/endpoints" + "net/url" + "gocloud.dev/blob" "gocloud.dev/blob/s3blob" "log" @@ -19,6 +21,19 @@ import ( "sync" ) +// AWS Crud +type Resolver struct { + URL *url.URL +} + +func (r *Resolver) ResolveEndpoint(_ context.Context, params s3.EndpointParameters) (transport.Endpoint, error) { + u := *r.URL + u.Path += "/" + *params.Bucket + return transport.Endpoint{URI: u}, nil +} + +// + type CloudStorage struct { BucketRef *blob.Bucket BucketName string @@ -134,39 +149,54 @@ func NewCloudStorage(c context.Context) (Storage, error) { //Pattern specifically for Self hosted S3 compatible instances Minio / Ceph if boolStrCheck(getMapValue(Custom, "false", stringEmpty, appData)) { - var sess *session.Session - creds := credentials.NewStaticCredentials( + creds := credentials.NewStaticCredentialsProvider( getMapValue(AccessId, os.Getenv("AWS_ACCESS_KEY"), stringEmpty, appData), getMapValue(SecretKey, os.Getenv("AWS_SECRET_KEY"), stringEmpty, appData), "") - sess, err = session.NewSession(&aws.Config{ - Credentials: creds, - Endpoint: aws.String(getMapValue(Endpoint, "http://localhost:9000", stringEmpty, appData)), - DisableSSL: aws.Bool(getMapValue(SSLEnabled, "false", stringEmpty, appData) != "true"), - S3ForcePathStyle: aws.Bool(true), - Region: aws.String(getMapValue(Region, "us-east-1", stringEmpty, appData)), - }) - if err != nil { - errorMsg = err.Error() + host := getMapValue(Endpoint, "http://localhost:9000", stringEmpty, appData) + cloudCfg := &aws.Config{ + Credentials: creds, + Region: getMapValue(Region, "us-east-1", stringEmpty, appData), + BaseEndpoint: &host, + } + session := s3.NewFromConfig(*cloudCfg, + func(o *s3.Options) { + o.UsePathStyle = true // <---- here + }, + func(o *s3.Options) { + endpointURL, _ := url.Parse(host) // or where ever you ran minio + s3.WithEndpointResolverV2(&Resolver{URL: endpointURL}) + }, + ) + if session == nil { + errorMsg = "No valid session could be created" } - bucketObj, err = s3blob.OpenBucket(context.Background(), sess, appData["bucket_name"], nil) + bucketObj, err = s3blob.OpenBucketV2(context.Background(), session, appData["bucket_name"], nil) if err != nil { errorMsg = err.Error() } if err == nil && boolStrCheck(getMapValue(InitBucket, "false", stringEmpty, appData)) { + slog.Info("attempting to bootstrap bucket", slog.Any("bucket", appData[BucketName])) //Attempts to initiate bucket - initBucketOnce.Do(func() { - client := s3.New(sess) + createBucket := func() { m := s3.CreateBucketInput{ Bucket: aws.String(appData[BucketName]), } //attempt to create bucket - _, err := client.CreateBucket(&m) + _, err := session.CreateBucket(context.Background(), &m) if err != nil { slog.Warn("bucket already exists or cannot be created", "bucket", *m.Bucket) } else { slog.Info("bucket has been created", "bucket", *m.Bucket) } - }) + } + + if os.Getenv("TESTING") != "1" { + initBucketOnce.Do(func() { + createBucket() + }) + } else { + createBucket() + } } diff --git a/internal/service/teams.go b/internal/service/teams.go index 530c47e6..8ef93474 100644 --- a/internal/service/teams.go +++ b/internal/service/teams.go @@ -256,11 +256,11 @@ func (s *DashNGoImpl) addTeamMember(team *models.TeamDTO, userDTO *models.TeamMe if userDTO.Permission == AdminUserPermission { adminPatch := teams.NewUpdateTeamMemberParams() adminPatch.TeamID = fmt.Sprintf("%d", team.ID) - adminPatch.UserID = userDTO.UserID + adminPatch.UserID = user.ID adminPatch.Body = &models.UpdateTeamMemberCommand{Permission: AdminUserPermission} - response, err := s.GetClient().Teams.UpdateTeamMember(adminPatch) - if err != nil { - return "", err + response, updateErr := s.GetClient().Teams.UpdateTeamMember(adminPatch) + if updateErr != nil { + return "", updateErr } slog.Debug("Updated permissions for user on team ", "username", userDTO.Name, "teamName", team.Name, "message", response.GetPayload().Message) } diff --git a/internal/service/user.go b/internal/service/user.go index 99316841..76b65c18 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -151,7 +151,7 @@ func (s *DashNGoImpl) UploadUsers(filter filters.Filter) []types.UserProfileWith slog.Error("unable to read user back from grafana", "username", newUser.Email, "userID", userCreated.GetPayload().ID) continue } - userListings = append(userListings, types.UserProfileWithAuth{UserProfileDTO: *resp.GetPayload(), Password: newUser.Password}) + userListings = append(userListings, types.UserProfileWithAuth{UserProfileDTO: *resp.GetPayload(), Password: string(newUser.Password)}) } } diff --git a/internal/templating/templating_test.go b/internal/templating/templating_test.go index aa5d8392..a0a3407c 100644 --- a/internal/templating/templating_test.go +++ b/internal/templating/templating_test.go @@ -3,6 +3,7 @@ package templating import ( "github.com/esnet/gdg/internal/config" "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling/common" "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" "os" @@ -18,7 +19,7 @@ func TestGenerate(t *testing.T) { if strings.Contains(dir, "templating") { os.Chdir("../..") } - config.InitGdgConfig("testing.yml", "") + config.InitGdgConfig(common.DefaultTestConfig, "") config.InitTemplateConfig("templates-example") template := NewTemplate() data, err := template.Generate("template_example") diff --git a/internal/types/models.go b/internal/types/models.go index 60b4c219..2a1cb08e 100644 --- a/internal/types/models.go +++ b/internal/types/models.go @@ -16,3 +16,8 @@ type OrgsDTOWithPreferences struct { Organization *models.OrgDTO `json:"organization"` Preferences *models.Preferences `json:"preferences"` // Preferences are preferences associated with a given org. theme, dashboard, timezone, etc } + +type ConnectionPermissionItem struct { + Connection *models.DataSourceListItemDTO + Permissions []*models.ResourcePermissionDTO +} diff --git a/pkg/test_tooling/common.go b/pkg/test_tooling/common.go new file mode 100644 index 00000000..283a1a51 --- /dev/null +++ b/pkg/test_tooling/common.go @@ -0,0 +1,122 @@ +package test_tooling + +import ( + "context" + "fmt" + "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling/common" + "github.com/esnet/gdg/pkg/test_tooling/containers" + "github.com/google/uuid" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/testcontainers/testcontainers-go" + "gopkg.in/yaml.v3" + "log/slog" + "os" + "strings" + "testing" +) + +const ( + GrafanaTestVersionEnv = "GRAFANA_TEST_VERSION" + + // #nosec G101 + EnableTokenTestsEnv = "TEST_TOKEN_CONFIG" +) + +func InitTest(t *testing.T, cfgName *string, useEnterprise bool) (service.GrafanaService, *viper.Viper, testcontainers.Container, func() error) { + var ( + additionalEnv map[string]string + suffix string + err error + ) + + if useEnterprise { + additionalEnv, err = containers.SetupGrafanaLicense() + if err != nil { + slog.Error("no valid grafana license found, skipping enterprise tests") + t.Skip() + } + suffix = "-enterprise" + } + localGrafanaContainer, cancel := containers.SetupGrafanaContainer(additionalEnv, "", suffix) + apiClient, v := CreateSimpleClient(t, cfgName, localGrafanaContainer) + noOp := func() error { + cancel() + return nil + } + + if os.Getenv(EnableTokenTestsEnv) != "1" { + return apiClient, v, localGrafanaContainer, noOp + } + + testData, _ := os.ReadFile(v.ConfigFileUsed()) + data := map[string]interface{}{} + err = yaml.Unmarshal(testData, &data) + assert.Nil(t, err) + + apiClient.DeleteAllTokens() //Remove any old data + tokenName, _ := uuid.NewUUID() + newKey, err := apiClient.CreateAPIKey(tokenName.String(), "admin", 0) + assert.Nil(t, err) + + wrapper := map[string]*config.GrafanaConfig{} + _ = wrapper + + level1 := data["contexts"].(map[string]interface{}) + level2 := level1["testing"].(map[string]interface{}) + level2["token"] = newKey.Key + level2["user_name"] = "" + level2["password"] = "" + + updatedCfg, err := yaml.Marshal(data) + assert.Nil(t, err) + tokenCfg, err := os.CreateTemp("config", "token*.yml") + assert.Nil(t, err, "Unable to create token configuration file") + newCfg := tokenCfg.Name() + err = os.WriteFile(newCfg, updatedCfg, 0600) + assert.Nil(t, err) + + cleanUp := func() error { + cancel() + return os.Remove(newCfg) + } + + apiClient, v = CreateSimpleClient(t, &newCfg, localGrafanaContainer) + return apiClient, v, localGrafanaContainer, cleanUp + +} + +func CreateSimpleClient(t *testing.T, cfgName *string, container testcontainers.Container) (service.GrafanaService, *viper.Viper) { + if cfgName == nil { + cfgName = new(string) + *cfgName = common.DefaultTestConfig + } + + actualPort, err := container.Endpoint(context.Background(), "") + grafanaHost := fmt.Sprintf("http://%s", actualPort) + err = os.Setenv("GDG_CONTEXTS__TESTING__URL", grafanaHost) + assert.Nil(t, err) + dockerContainer, ok := container.(*testcontainers.DockerContainer) + if ok { + slog.Info("Grafana Test container running", slog.String("host", grafanaHost+"/login"), slog.String("imageVersion", dockerContainer.Image)) + } + + config.InitGdgConfig(*cfgName, "'") + conf := config.Config().GetViperConfig(config.ViperGdgConfig) + assert.NotNil(t, conf) + //Hack for Local testing + contextName := conf.GetString("context_name") + conf.Set(fmt.Sprintf("context.%s.url", contextName), grafanaHost) + assert.Equal(t, contextName, "testing") + client := service.NewApiService("dummy") + path, _ := os.Getwd() + if strings.Contains(path, "test") { + err := os.Chdir("..") + if err != nil { + slog.Warn("unable to set directory to parent") + } + } + return client, conf +} diff --git a/pkg/test_tooling/common/const.go b/pkg/test_tooling/common/const.go new file mode 100644 index 00000000..536c8e0e --- /dev/null +++ b/pkg/test_tooling/common/const.go @@ -0,0 +1,5 @@ +package common + +const ( + DefaultTestConfig = "testing.yml" +) diff --git a/pkg/test_tooling/containers.go b/pkg/test_tooling/containers.go new file mode 100644 index 00000000..26584e4c --- /dev/null +++ b/pkg/test_tooling/containers.go @@ -0,0 +1,65 @@ +package test_tooling + +import ( + "context" + "fmt" + "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling/containers" + "log" + "log/slog" + "os" +) + +func SetupCloudFunction(params []string) (context.Context, context.CancelFunc, service.GrafanaService, error) { + errorFunc := func(err error) (context.Context, context.CancelFunc, service.GrafanaService, error) { + return nil, nil, nil, err + } + _ = os.Setenv(service.InitBucket, "true") + bucketName := params[1] + container, cancel := containers.BootstrapCloudStorage("", "") + wwwPort, err := container.PortEndpoint(context.Background(), "9001", "") + if err != nil { + return errorFunc(err) + } + actualPort, err := container.Endpoint(context.Background(), "") + if err != nil { + return errorFunc(err) + } + minioHost := fmt.Sprintf("http://%s", actualPort) + slog.Info("Minio container is up and running", slog.Any("hostname", fmt.Sprintf("http://%s", wwwPort))) + var m = map[string]string{ + service.InitBucket: "true", + service.CloudType: params[0], + service.Prefix: "dummy", + service.AccessId: "test", + service.SecretKey: "secretsss", + service.BucketName: bucketName, + service.Kind: "cloud", + service.Custom: "true", + service.Endpoint: minioHost, + service.SSLEnabled: "false", + } + + cfgObj := config.Config().GetGDGConfig() + defaultCfg := config.Config().GetDefaultGrafanaConfig() + defaultCfg.Storage = "test" + cfgObj.StorageEngine["test"] = m + apiClient := service.NewApiService("dummy") + + ctx := context.Background() + ctx = context.WithValue(ctx, service.StorageContext, m) + configMap := map[string]string{} + for key, value := range m { + configMap[key] = fmt.Sprintf("%v", value) + } + + s, err := service.NewCloudStorage(ctx) + if err != nil { + log.Fatalf("Could not instantiate cloud storage for type: %s", params[0]) + } + dash := apiClient.(*service.DashNGoImpl) + dash.SetStorage(s) + + return ctx, cancel, apiClient, nil +} diff --git a/pkg/test_tooling/containers/containers.go b/pkg/test_tooling/containers/containers.go new file mode 100644 index 00000000..ff9a3d32 --- /dev/null +++ b/pkg/test_tooling/containers/containers.go @@ -0,0 +1,121 @@ +package containers + +import ( + "context" + "errors" + "fmt" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + "log" + "log/slog" + "maps" + "os" +) + +const ( + defaultGrafanaVersion = "11.1.4-ubuntu" + defaultGrafanaVersionEnv = "GRAFANA_TEST_VERSION" + enterpriseLicenceKey = "GF_ENTERPRISE_LICENSE_TEXT" + enterpriseLicenceKeyEnv = "ENTERPRISE_LICENSE" + DefaultCloudUser = "test" + DefaultCloudPass = "secretsss" +) + +func BootstrapCloudStorage(username, password string) (testcontainers.Container, context.CancelFunc) { + if username == "" || password == "" { + username = DefaultCloudUser + password = DefaultCloudPass + } + + ctx := context.Background() + req := testcontainers.ContainerRequest{ + Image: "bitnami/minio:latest", + ExposedPorts: []string{"9000/tcp", "9001/tcp"}, + Env: map[string]string{"MINIO_ROOT_USER": username, "MINIO_ROOT_PASSWORD": password}, + WaitingFor: wait.ForListeningPort("9000/tcp"), + } + minioC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + panic(err) + } + + cancel := func() { + if err := minioC.Terminate(ctx); err != nil { + panic(err) + } else { + slog.Info("Minio container has been terminated") + } + } + return minioC, cancel + +} + +func SetupGrafanaLicense() (map[string]string, error) { + val := os.Getenv(enterpriseLicenceKeyEnv) + m := map[string]string{enterpriseLicenceKey: val} + if val == "" { + return nil, errors.New("no valid enterprise license found") + } + return m, nil +} + +func SetupGrafanaContainer(additionalEnvProps map[string]string, version, imageSuffix string) (testcontainers.Container, func()) { + retry := func() (testcontainers.Container, func(), error) { + defaultProps := map[string]string{ + "GF_INSTALL_PLUGINS": "grafana-googlesheets-datasource", + "GF_AUTH_ANONYMOUS_ENABLED": "true", + "GF_SECURITY_ADMIN_PASSWORD": "admin", // This is a no-op right now, but we should trickle this up to + // allow setting grafana admin credentials. + } + if version == "" { + version = os.Getenv(defaultGrafanaVersionEnv) + if version == "" { + version = defaultGrafanaVersion + } + } + //merge properties + maps.Copy(defaultProps, additionalEnvProps) + ctx := context.Background() + req := testcontainers.ContainerRequest{ + Image: fmt.Sprintf("grafana/grafana%s:%s", imageSuffix, version), + ExposedPorts: []string{"3000/tcp"}, + Env: defaultProps, + WaitingFor: wait.ForListeningPort("3000/tcp"), + } + grafanaC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + + if err != nil { + return nil, nil, fmt.Errorf("failed to retrieve valid container, %w", err) + } + + cancel := func() { + if err := grafanaC.Terminate(ctx); err != nil { + slog.Warn("unable to terminate previous container", slog.Any("err", err)) + } else { + slog.Info("Grafana Container has been terminated") + } + } + + return grafanaC, cancel, nil + } + + //retry a few times just in case. + for i := 0; i < 3; i++ { + container, cancelFn, err := retry() + if err == nil { + return container, cancelFn + } else { + slog.Error(err.Error()) + } + } + + log.Fatal("Unable to start container") + return nil, nil + +} diff --git a/pkg/test_tooling/path/path.go b/pkg/test_tooling/path/path.go new file mode 100644 index 00000000..697fc118 --- /dev/null +++ b/pkg/test_tooling/path/path.go @@ -0,0 +1,23 @@ +package path + +import ( + "os" + "strings" +) + +const testEnv = "TESTING" + +func FixTestDir(packageName string, newPath string) error { + err := os.Setenv(testEnv, "1") + if err != nil { + return err + } + dir, _ := os.Getwd() + if strings.Contains(dir, packageName) { + err = os.Chdir(newPath) + if err != nil { + return err + } + } + return nil +} diff --git a/test/cloud_integration_test.go b/test/cloud_integration_test.go index dde17228..2525dc96 100644 --- a/test/cloud_integration_test.go +++ b/test/cloud_integration_test.go @@ -1,7 +1,10 @@ package test import ( + "context" "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling" + "github.com/esnet/gdg/pkg/test_tooling/path" "github.com/stretchr/testify/assert" _ "gocloud.dev/blob/memblob" "log/slog" @@ -10,11 +13,9 @@ import ( ) func TestCloudDataSourceCRUD(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - - apiClient, _, cleanup := initTest(t, nil) + assert.NoError(t, path.FixTestDir("test", "..")) + t.Log("Running Cloud Tests") + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer func() { cleanErr := cleanup() if cleanErr != nil { @@ -28,8 +29,9 @@ func TestCloudDataSourceCRUD(t *testing.T) { apiClient.UploadConnections(dsFilter) dsList := apiClient.ListConnections(dsFilter) assert.True(t, len(dsList) > 0) - SetupCloudFunction([]string{"s3", "testing"}) - //SetupCloudFunction(apiClient, []string{"mem", "testing"}) + _, cancel, apiClient, err := test_tooling.SetupCloudFunction([]string{"s3", "testing"}) + assert.NoError(t, err) + defer cancel() slog.Info("Importing DataSources") dsStringList := apiClient.DownloadConnections(dsFilter) //Saving to S3 @@ -49,19 +51,12 @@ func TestCloudDataSourceCRUD(t *testing.T) { // TestDashboardCloudCrud will load testing_data to Grafana from local context. Switch to CLoud, // Save all data to Cloud, wipe grafana and reload data back into grafana and validate func TestDashboardCloudCRUD(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - err := os.Setenv("GDG_CONTEXT_NAME", "testing") - assert.Nil(t, err, "Failed to set context name via env to testing") - - apiClient, _, cleanup := initTest(t, nil) - defer func() { - cleanErr := cleanup() - if cleanErr != nil { - slog.Error("unable to clean up after test", slog.Any("err", cleanErr)) - } - }() + assert.NoError(t, os.Setenv("GDG_CONTEXT_NAME", "testing")) + assert.NoError(t, path.FixTestDir("test", "..")) + var err error + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) + defer cleanup() + //defer cleanup, "Failed to cleanup test containers for %s", t.Name()) //Wipe all data from grafana dashFilter := service.NewDashboardFilter("", "", "") apiClient.DeleteAllDashboards(dashFilter) @@ -69,8 +64,11 @@ func TestDashboardCloudCRUD(t *testing.T) { apiClient.UploadDashboards(dashFilter) boards := apiClient.ListDashboards(dashFilter) assert.True(t, len(boards) > 0) + var cancel context.CancelFunc - _, apiClient = SetupCloudFunction([]string{"s3", "testing"}) + _, cancel, apiClient, err = test_tooling.SetupCloudFunction([]string{"s3", "testing"}) + assert.NoError(t, err) + defer cancel() //At this point all operations are reading/writing from Minio slog.Info("Importing Dashboards") diff --git a/test/common_test.go b/test/common_test.go index 23607c1f..850a8443 100644 --- a/test/common_test.go +++ b/test/common_test.go @@ -1,249 +1,46 @@ package test import ( - "context" - "fmt" - "github.com/esnet/gdg/internal/config" - "github.com/esnet/gdg/internal/service" - "github.com/google/uuid" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" - "golang.org/x/exp/maps" - "gopkg.in/yaml.v3" - "log" + "github.com/esnet/gdg/pkg/test_tooling" + "github.com/esnet/gdg/pkg/test_tooling/path" + "github.com/joho/godotenv" "log/slog" "os" - "slices" - "sync" - "time" - - "strings" "testing" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" ) -var minioContainer testcontainers.Container -var grafnaContainer testcontainers.Container - -type Containers struct { - Cancel context.CancelFunc - Container testcontainers.Container -} - -func setupMinioContainer(wg *sync.WaitGroup, channels chan Containers) { - // pulls an image, creates a container based on it and runs it - defer wg.Done() - - ctx := context.Background() - req := testcontainers.ContainerRequest{ - Image: "bitnami/minio:latest", - ExposedPorts: []string{"9000/tcp"}, - Env: map[string]string{"MINIO_ROOT_USER": "test", "MINIO_ROOT_PASSWORD": "secretsss"}, - WaitingFor: wait.ForListeningPort("9000/tcp"), - } - minioC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ - ContainerRequest: req, - Started: true, - }) - if err != nil { - panic(err) - } - - slog.Info("Minio container is up and running") - cancel := func() { - if err := minioC.Terminate(ctx); err != nil { - panic(err) - } else { - slog.Info("Minio container has been terminated") - } - } - result := Containers{ - Cancel: cancel, - Container: minioC, - } - channels <- result - -} - -func setupGrafanaContainer(wg *sync.WaitGroup, channels chan Containers) { - // pulls an image, creates a container based on it and runs it - defer wg.Done() - ctx := context.Background() - req := testcontainers.ContainerRequest{ - Image: "grafana/grafana:10.0.0-ubuntu", - ExposedPorts: []string{"3000/tcp"}, - Env: map[string]string{ - "GF_INSTALL_PLUGINS": "grafana-googlesheets-datasource", - "GF_AUTH_ANONYMOUS_ENABLED": "true", - }, - WaitingFor: wait.ForListeningPort("3000/tcp"), - } - grafanaC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ - ContainerRequest: req, - Started: true, - }) +func TestMain(m *testing.M) { + err := path.FixTestDir("test", "..") if err != nil { panic(err) } - cancel := func() { - if err := grafanaC.Terminate(ctx); err != nil { - panic(err) - } else { - slog.Info("Grafana Container has been terminated") - } - } - result := Containers{ - Cancel: cancel, - Container: grafanaC, - } - channels <- result - -} - -func TestMain(m *testing.M) { - channels := make(chan Containers, 2) - var wg = new(sync.WaitGroup) - wg.Add(2) - slog.Info("Starting at", "time", time.Now().String()) - go setupMinioContainer(wg, channels) - go setupGrafanaContainer(wg, channels) - wg.Wait() - close(channels) - slog.Info("Ending at", "end", time.Now().String()) - - for entry := range channels { - defer entry.Cancel() - str, err := entry.Container.Ports(context.Background()) - if err != nil { - slog.Error("unable to obtain bound ports for container") - continue - } - keys := maps.Keys(str) - if slices.Contains(keys, "9000/tcp") { - minioContainer = entry.Container - } - if slices.Contains(keys, "3000/tcp") { - grafnaContainer = entry.Container - - } - - } - exitVal := m.Run() - - os.Exit(exitVal) -} - -func initTest(t *testing.T, cfgName *string) (service.GrafanaService, *viper.Viper, func() error) { - apiClient, v := createSimpleClient(t, cfgName) - noOp := func() error { return nil } - - if os.Getenv("TEST_TOKEN_CONFIG") != "1" { - return apiClient, v, noOp - } - - testData, _ := os.ReadFile(v.ConfigFileUsed()) - data := map[string]interface{}{} - err := yaml.Unmarshal(testData, &data) - assert.Nil(t, err) - - apiClient.DeleteAllTokens() //Remove any old data - tokenName, _ := uuid.NewUUID() - newKey, err := apiClient.CreateAPIKey(tokenName.String(), "admin", 0) - assert.Nil(t, err) - - wrapper := map[string]*config.GrafanaConfig{} - _ = wrapper - - level1 := data["contexts"].(map[string]interface{}) - level2 := level1["testing"].(map[string]interface{}) - level2["token"] = newKey.Key - level2["user_name"] = "" - level2["password"] = "" - - updatedCfg, err := yaml.Marshal(data) - assert.Nil(t, err) - tokenCfg, err := os.CreateTemp("config", "token*.yml") - assert.Nil(t, err, "Unable to create token configuration file") - newCfg := tokenCfg.Name() - err = os.WriteFile(newCfg, updatedCfg, 0644) - assert.Nil(t, err) - - cleanUp := func() error { - return os.Remove(newCfg) - } - apiClient, v = createSimpleClient(t, &newCfg) - return apiClient, v, cleanUp - -} - -func createSimpleClient(t *testing.T, cfgName *string) (service.GrafanaService, *viper.Viper) { - if cfgName == nil { - cfgName = new(string) - *cfgName = "testing.yml" - } - - actualPort, err := grafnaContainer.Endpoint(context.Background(), "") - err = os.Setenv("GDG_CONTEXTS__TESTING__URL", fmt.Sprintf("http://%s", actualPort)) - assert.Nil(t, err) - - config.InitGdgConfig(*cfgName, "'") - conf := config.Config().GetViperConfig(config.ViperGdgConfig) - assert.NotNil(t, conf) - //Hack for Local testing - contextName := conf.GetString("context_name") - conf.Set(fmt.Sprintf("context.%s.url", contextName), fmt.Sprintf("http://localhost:%s", actualPort)) - assert.Equal(t, contextName, "testing") - client := service.NewApiService("dummy") - path, _ := os.Getwd() - if strings.Contains(path, "test") { - err := os.Chdir("..") - if err != nil { - slog.Warn("unable to set directory to parent") + err = godotenv.Load(".env") + //set global log level + slog.SetLogLoggerLevel(slog.LevelDebug) // Set global log level to Debug + grafanaTestVersions := []string{"10.2.3-ubuntu", "11.1.5-ubuntu"} + testModes := []string{"basicAuth", "token"} + if os.Getenv("DEVELOPER") == "1" { + slog.Debug("Limiting to single testMode and grafana version", slog.Any("grafanaVersion", grafanaTestVersions[1]), slog.String("testMode", testModes[0])) + grafanaTestVersions = grafanaTestVersions[1:] + testModes = testModes[0:1] + } + + for _, version := range grafanaTestVersions { + for _, i := range testModes { + os.Setenv(test_tooling.GrafanaTestVersionEnv, version) + if i == "token" { + os.Setenv(test_tooling.EnableTokenTestsEnv, "1") + } else { + os.Setenv(test_tooling.EnableTokenTestsEnv, "0") + } + slog.Info("Running Test suit for", + slog.Any("grafanaVersion", version), + slog.Any("AuthMode", i)) + exitVal := m.Run() + if exitVal != 0 { + panic("Failed to run test with token value of: " + i) + } } } - return client, conf -} - -func SetupCloudFunction(params []string) (context.Context, service.GrafanaService) { - _ = os.Setenv(service.InitBucket, "true") - bucketName := params[1] - - actualPort, err := minioContainer.Endpoint(context.Background(), "") - var m = map[string]string{ - service.InitBucket: "true", - service.CloudType: params[0], - service.Prefix: "dummy", - service.AccessId: "test", - service.SecretKey: "secretsss", - service.BucketName: bucketName, - service.Kind: "cloud", - service.Custom: "true", - service.Endpoint: fmt.Sprintf("http://%s", actualPort), - service.SSLEnabled: "false", - } - - cfgObj := config.Config().GetGDGConfig() - defaultCfg := config.Config().GetDefaultGrafanaConfig() - defaultCfg.Storage = "test" - cfgObj.StorageEngine["test"] = m - apiClient := service.NewApiService("dummy") - - ctx := context.Background() - ctx = context.WithValue(ctx, service.StorageContext, m) - configMap := map[string]string{} - for key, value := range m { - configMap[key] = fmt.Sprintf("%v", value) - } - - s, err := service.NewCloudStorage(ctx) - if err != nil { - log.Fatalf("Could not instantiate cloud storage for type: %s", params[0]) - } - dash := apiClient.(*service.DashNGoImpl) - dash.SetStorage(s) - - return ctx, apiClient } diff --git a/test/connections_integration_test.go b/test/connections_integration_test.go index 210b37d2..d2bee0e7 100644 --- a/test/connections_integration_test.go +++ b/test/connections_integration_test.go @@ -3,20 +3,99 @@ package test import ( "github.com/esnet/gdg/internal/config" "github.com/esnet/gdg/internal/service" - + "github.com/esnet/gdg/internal/types" + "github.com/esnet/gdg/pkg/test_tooling" "github.com/grafana/grafana-openapi-client-go/models" "log/slog" + "os" + "strings" "testing" "github.com/stretchr/testify/assert" ) -func TestConnectionsCRUD(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") +func TestConnectionPermissionsCrud(t *testing.T) { + if os.Getenv(test_tooling.EnableTokenTestsEnv) == "1" { + t.Skip("Skipping Token configuration, Team and User CRUD requires Basic SecureData") + } + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, true) + defer cleanup() + //Upload all connections + filtersEntity := service.NewConnectionFilter("") + connectionsAdded := apiClient.UploadConnections(filtersEntity) + assert.Equal(t, len(connectionsAdded), 3) + //Upload all users + newUsers := apiClient.UploadUsers(service.NewUserFilter("")) + assert.Equal(t, len(newUsers), 2) + //Upload all teams + filter := service.NewTeamFilter("") + teams := apiClient.UploadTeams(filter) + assert.Equal(t, len(teams), 2) + //Get current Permissions + permissionFilters := service.NewConnectionFilter("") + currentPerms := apiClient.ListConnectionPermissions(permissionFilters) + assert.Equal(t, len(currentPerms), 3) + var entry *types.ConnectionPermissionItem + for ndx, item := range currentPerms { + if item.Connection.Name == "Google Sheets" { + entry = ¤tPerms[ndx] + break + } + } + assert.NotNil(t, entry) + assert.Equal(t, len(entry.Permissions), 4) + + removed := apiClient.DeleteAllConnectionPermissions(permissionFilters) + assert.Equal(t, len(removed), 3) + currentPerms = apiClient.ListConnectionPermissions(permissionFilters) + for ndx, item := range currentPerms { + if item.Connection.Name == "Google Sheets" { + entry = ¤tPerms[ndx] + break + } + } + assert.Equal(t, 2, len(entry.Permissions)) + updated := apiClient.UploadConnectionPermissions(permissionFilters) + assert.Equal(t, 3, len(updated)) + currentPerms = apiClient.ListConnectionPermissions(permissionFilters) + for ndx, item := range currentPerms { + if item.Connection.Name == "Google Sheets" { + entry = ¤tPerms[ndx] + break + } } + assert.Equal(t, len(entry.Permissions), 7) + currentPerms = apiClient.ListConnectionPermissions(permissionFilters) + var foundTux, foundBob, foundTeam bool + for _, item := range entry.Permissions { + if item.UserLogin == "tux" { + foundTux = true + assert.Equal(t, item.Permission, "Admin") + assert.Equal(t, len(item.Actions), 8) + assert.True(t, strings.Contains(item.RoleName, "managed:users")) + assert.True(t, strings.Contains(item.RoleName, "permissions")) + } else if item.UserLogin == "bob" { + foundBob = true + assert.Equal(t, item.Permission, "Edit") + assert.Equal(t, len(item.Actions), 4) + assert.True(t, strings.Contains(item.RoleName, "managed:users")) + assert.True(t, strings.Contains(item.RoleName, "permissions")) + } else if item.Team == "musicians" { + foundTeam = true + assert.Equal(t, item.Permission, "Query") + assert.Equal(t, len(item.Actions), 2) + assert.True(t, strings.Contains(item.RoleName, "managed:teams")) + assert.True(t, strings.Contains(item.RoleName, "permissions")) + } + } + assert.True(t, foundTux) + assert.True(t, foundBob) + assert.True(t, foundTeam) - apiClient, _, cleanup := initTest(t, nil) +} + +func TestConnectionsCRUD(t *testing.T) { + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer func() { cleanErr := cleanup() if cleanErr != nil { @@ -55,7 +134,7 @@ func TestConnectionFilter(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - _, _, cleanup := initTest(t, nil) + _, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer func() { cleanErr := cleanup() if cleanErr != nil { diff --git a/test/dashboard_integration_test.go b/test/dashboard_integration_test.go index 7cb410e5..cc7210fc 100644 --- a/test/dashboard_integration_test.go +++ b/test/dashboard_integration_test.go @@ -5,6 +5,7 @@ import ( "github.com/esnet/gdg/internal/config" "github.com/esnet/gdg/internal/service" "github.com/esnet/gdg/internal/service/filters" + "github.com/esnet/gdg/pkg/test_tooling" "github.com/grafana/grafana-openapi-client-go/models" "os" "strings" @@ -19,7 +20,7 @@ func TestDashboardCRUD(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer func() { err := cleanup() if err != nil { @@ -78,7 +79,7 @@ func TestDashboardCRUDTags(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, container, cleanup := test_tooling.InitTest(t, nil, false) defer func() { err := cleanup() if err != nil { @@ -113,7 +114,7 @@ func TestDashboardCRUDTags(t *testing.T) { // os.Setenv("GDG_CONTEXTS__TESTING__IGNORE_FILTERS", "true") defer os.Unsetenv("") - apiClient, _ = createSimpleClient(t, nil) + apiClient, _ = test_tooling.CreateSimpleClient(t, nil, container) filterNone := service.NewDashboardFilter("", "", "") apiClient.UploadDashboards(filterNone) //Listing with no filter @@ -142,7 +143,7 @@ func TestDashboardTagsFilter(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() emptyFilter := filters.NewBaseFilter() @@ -181,7 +182,7 @@ func TestWildcardFilter(t *testing.T) { } // Setup Filters - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() emptyFilter := service.NewDashboardFilter("", "", "") diff --git a/test/data/org_main-org/connections-permissions/google-sheets.json b/test/data/org_main-org/connections-permissions/google-sheets.json new file mode 100644 index 00000000..c6b58ee8 --- /dev/null +++ b/test/data/org_main-org/connections-permissions/google-sheets.json @@ -0,0 +1,121 @@ +{ + "Connection": { + "access": "proxy", + "id": 1, + "jsonData": { + "authType": "key" + }, + "name": "Google Sheets", + "orgId": 1, + "type": "grafana-googlesheets-datasource", + "typeLogoUrl": "public/plugins/grafana-googlesheets-datasource/img/sheets.svg", + "typeName": "Google Sheets", + "uid": "uL86Byf4k" + }, + "Permissions": [ + { + "actions": [ + "datasources.permissions:read", + "datasources.permissions:write", + "datasources.caching:read", + "datasources.caching:write", + "datasources:read", + "datasources:query", + "datasources:write", + "datasources:delete" + ], + "id": 560, + "isManaged": true, + "permission": "Admin", + "roleName": "managed:users:1:permissions", + "userAvatarUrl": "/avatar/46d229b033af06a191ff2267bca9ae56", + "userId": 1, + "userLogin": "admin" + }, + { + "actions": [ + "datasources.caching:read", + "datasources.caching:write", + "datasources:read", + "datasources:query", + "datasources:write", + "datasources:delete", + "datasources.permissions:read", + "datasources.permissions:write" + ], + "id": 607, + "isManaged": true, + "permission": "Admin", + "roleName": "managed:users:3:permissions", + "userAvatarUrl": "/avatar/b0af9e9c1c0acf1dc93d4dc9d6bcafb1", + "userId": 3, + "userLogin": "tux" + }, + { + "actions": [ + "datasources:delete", + "datasources:read", + "datasources:query", + "datasources:write" + ], + "id": 615, + "isManaged": true, + "permission": "Edit", + "roleName": "managed:users:2:permissions", + "userAvatarUrl": "/avatar/64029c12d260bd5e3cb2504f4155998f", + "userId": 2, + "userLogin": "bob" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "id": 619, + "isManaged": true, + "permission": "Query", + "roleName": "managed:teams:2:permissions", + "team": "musicians", + "teamAvatarUrl": "/avatar/80ca0095115d85b522e59fa72b51a213", + "teamId": 2 + }, + { + "actions": [ + "datasources.permissions:write", + "datasources.caching:write", + "datasources:delete", + "datasources.permissions:read", + "datasources:write", + "datasources:query", + "datasources:read", + "datasources.caching:read" + ], + "builtInRole": "Admin", + "id": 350, + "permission": "Admin", + "roleName": "basic:admin" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Viewer", + "id": 556, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:viewer:permissions" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Editor", + "id": 558, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:editor:permissions" + } + ] +} \ No newline at end of file diff --git a/test/data/org_main-org/connections-permissions/netsage-tsds.json b/test/data/org_main-org/connections-permissions/netsage-tsds.json new file mode 100644 index 00000000..13a2b3ba --- /dev/null +++ b/test/data/org_main-org/connections-permissions/netsage-tsds.json @@ -0,0 +1,75 @@ +{ + "Connection": { + "access": "proxy", + "id": 2, + "jsonData": { + "keepCookies": [] + }, + "name": "Netsage TSDS", + "orgId": 1, + "type": "globalnoc-tsds-datasource", + "typeLogoUrl": "public/img/icn-datasource.svg", + "typeName": "globalnoc-tsds-datasource", + "uid": "i6uqEqE7k", + "url": "https://netsage-archive.grnoc.iu.edu/irnc/services/" + }, + "Permissions": [ + { + "actions": [ + "datasources:write", + "datasources:delete", + "datasources.permissions:read", + "datasources.permissions:write", + "datasources.caching:read", + "datasources.caching:write", + "datasources:read", + "datasources:query" + ], + "id": 572, + "isManaged": true, + "permission": "Admin", + "roleName": "managed:users:1:permissions", + "userAvatarUrl": "/avatar/46d229b033af06a191ff2267bca9ae56", + "userId": 1, + "userLogin": "admin" + }, + { + "actions": [ + "datasources.permissions:write", + "datasources.caching:write", + "datasources:delete", + "datasources.permissions:read", + "datasources:write", + "datasources:query", + "datasources:read", + "datasources.caching:read" + ], + "builtInRole": "Admin", + "id": 350, + "permission": "Admin", + "roleName": "basic:admin" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Viewer", + "id": 568, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:viewer:permissions" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Editor", + "id": 570, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:editor:permissions" + } + ] +} \ No newline at end of file diff --git a/test/data/org_main-org/connections-permissions/netsage.json b/test/data/org_main-org/connections-permissions/netsage.json new file mode 100644 index 00000000..9a36ca8a --- /dev/null +++ b/test/data/org_main-org/connections-permissions/netsage.json @@ -0,0 +1,82 @@ +{ + "Connection": { + "access": "proxy", + "database": "om-netsage-irnc-*", + "id": 3, + "isDefault": true, + "jsonData": { + "esVersion": 70, + "keepCookies": [], + "logLevelField": "", + "logMessageField": "", + "maxConcurrentShardRequests": "256", + "timeField": "start" + }, + "name": "netsage", + "orgId": 1, + "type": "elasticsearch", + "typeLogoUrl": "public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg", + "typeName": "Elasticsearch", + "uid": "rg9qPqP7z", + "url": "https://netsage-elk1.grnoc.iu.edu/esproxy2/" + }, + "Permissions": [ + { + "actions": [ + "datasources.permissions:write", + "datasources.caching:read", + "datasources.caching:write", + "datasources:read", + "datasources:query", + "datasources:write", + "datasources:delete", + "datasources.permissions:read" + ], + "id": 584, + "isManaged": true, + "permission": "Admin", + "roleName": "managed:users:1:permissions", + "userAvatarUrl": "/avatar/46d229b033af06a191ff2267bca9ae56", + "userId": 1, + "userLogin": "admin" + }, + { + "actions": [ + "datasources.permissions:write", + "datasources.caching:write", + "datasources:delete", + "datasources.permissions:read", + "datasources:write", + "datasources:query", + "datasources:read", + "datasources.caching:read" + ], + "builtInRole": "Admin", + "id": 350, + "permission": "Admin", + "roleName": "basic:admin" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Viewer", + "id": 580, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:viewer:permissions" + }, + { + "actions": [ + "datasources:read", + "datasources:query" + ], + "builtInRole": "Editor", + "id": 582, + "isManaged": true, + "permission": "Query", + "roleName": "managed:builtins:editor:permissions" + } + ] +} \ No newline at end of file diff --git a/test/data/org_main-org/connections/google-sheets.json b/test/data/org_main-org/connections/google-sheets.json index 1e122f74..87920f38 100644 --- a/test/data/org_main-org/connections/google-sheets.json +++ b/test/data/org_main-org/connections/google-sheets.json @@ -1,19 +1,14 @@ { - "id": 1, - "orgId": 1, - "uid": "uL86Byf4k", - "name": "Google Sheets", - "type": "grafana-googlesheets-datasource", - "typeLogoUrl": "public/plugins/grafana-googlesheets-datasource/img/sheets.svg", "access": "proxy", - "url": "", - "user": "", - "database": "", - "basicAuth": false, - "readOnly": false, - "isDefault": true, + "basicAuth": true, + "id": 1, "jsonData": { "authType": "key" }, - "secureJsonData": null + "name": "Google Sheets", + "orgId": 1, + "type": "grafana-googlesheets-datasource", + "typeLogoUrl": "public/plugins/grafana-googlesheets-datasource/img/sheets.svg", + "typeName": "Google Sheets", + "uid": "uL86Byf4k" } \ No newline at end of file diff --git a/test/data/org_main-org/connections/netsage-tsds.json b/test/data/org_main-org/connections/netsage-tsds.json index ff6e3d7e..3203b98d 100644 --- a/test/data/org_main-org/connections/netsage-tsds.json +++ b/test/data/org_main-org/connections/netsage-tsds.json @@ -1,20 +1,15 @@ { - "id": 9, - "orgId": 1, - "uid": "i6uqEqE7k", - "name": "Netsage TSDS", - "type": "globalnoc-tsds-datasource", - "typeLogoUrl": "public/img/icn-datasource.svg", "access": "proxy", - "url": "https://netsage-archive.grnoc.iu.edu/irnc/services/", - "password": "", - "user": "", - "database": "", - "basicAuth": false, - "readOnly": false, - "isDefault": false, + "basicAuth": true, + "id": 1, "jsonData": { "keepCookies": [] }, - "secureJsonData": null + "name": "Netsage TSDS", + "orgId": 1, + "type": "globalnoc-tsds-datasource", + "typeLogoUrl": "public/img/icn-datasource.svg", + "typeName": "globalnoc-tsds-datasource", + "uid": "i6uqEqE7k", + "url": "https://netsage-archive.grnoc.iu.edu/irnc/services/" } \ No newline at end of file diff --git a/test/data/org_main-org/connections/netsage.json b/test/data/org_main-org/connections/netsage.json index 8520a36d..0322d214 100644 --- a/test/data/org_main-org/connections/netsage.json +++ b/test/data/org_main-org/connections/netsage.json @@ -1,17 +1,8 @@ { - "id": 10, - "orgId": 1, - "uid": "rg9qPqP7z", - "name": "netsage", - "type": "elasticsearch", - "typeLogoUrl": "public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg", "access": "proxy", - "url": "https://netsage-elk1.grnoc.iu.edu/esproxy2/", - "password": "", - "user": "", - "database": "om-netsage-irnc-*", "basicAuth": true, - "readOnly": false, + "database": "om-netsage-irnc-*", + "id": 2, "isDefault": true, "jsonData": { "esVersion": 70, @@ -21,5 +12,11 @@ "maxConcurrentShardRequests": "256", "timeField": "start" }, - "secureJsonData": null + "name": "netsage", + "orgId": 1, + "type": "elasticsearch", + "typeLogoUrl": "public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg", + "typeName": "Elasticsearch", + "uid": "rg9qPqP7z", + "url": "https://netsage-elk1.grnoc.iu.edu/esproxy2/" } \ No newline at end of file diff --git a/test/data/users/bob.json b/test/data/users/bob.json new file mode 100644 index 00000000..1bc1f42c --- /dev/null +++ b/test/data/users/bob.json @@ -0,0 +1,8 @@ +{ + "authLabels": [], + "email": "bob@aol.com", + "id": 7, + "lastSeenAtAge": "5 years", + "login": "bob", + "name": "bob" +} diff --git a/test/folder_integration_test.go b/test/folder_integration_test.go index 707f7c5b..51dad38f 100644 --- a/test/folder_integration_test.go +++ b/test/folder_integration_test.go @@ -1,6 +1,7 @@ package test import ( + "github.com/esnet/gdg/pkg/test_tooling" "testing" "github.com/stretchr/testify/assert" @@ -11,7 +12,7 @@ func TestFolderCRUD(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() slog.Info("Exporting all folders") apiClient.UploadFolders(nil) diff --git a/test/libraryelements_integration_test.go b/test/libraryelements_integration_test.go index 73c4a1df..97287298 100644 --- a/test/libraryelements_integration_test.go +++ b/test/libraryelements_integration_test.go @@ -2,6 +2,7 @@ package test import ( "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling" "github.com/gosimple/slug" "github.com/grafana/grafana-openapi-client-go/models" "github.com/stretchr/testify/assert" @@ -14,9 +15,8 @@ func TestLibraryElementsCRUD(t *testing.T) { t.Skip("skipping integration test") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() - apiClient.DeleteAllDashboards(service.NewDashboardFilter("", "", "")) filtersEntity := service.NewDashboardFilter("", "", "") slog.Info("Exporting all Library Elements") apiClient.UploadLibraryElements(filtersEntity) diff --git a/test/license_test.go b/test/license_test.go new file mode 100644 index 00000000..58f620eb --- /dev/null +++ b/test/license_test.go @@ -0,0 +1,17 @@ +package test + +import ( + "github.com/esnet/gdg/pkg/test_tooling" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestLicenseEnterpriseCheck(t *testing.T) { + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) + defer cleanup() + assert.False(t, apiClient.IsEnterprise()) + enterpriseClient, _, _, enterpriseCleanup := test_tooling.InitTest(t, nil, true) + defer enterpriseCleanup() + assert.True(t, enterpriseClient.IsEnterprise()) + +} diff --git a/test/organizations_integration_test.go b/test/organizations_integration_test.go index 27878051..68959870 100644 --- a/test/organizations_integration_test.go +++ b/test/organizations_integration_test.go @@ -2,6 +2,7 @@ package test import ( "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling" "github.com/gosimple/slug" "github.com/grafana/grafana-openapi-client-go/models" "golang.org/x/exp/slices" @@ -19,7 +20,7 @@ func TestOrganizationCrud(t *testing.T) { if os.Getenv("TEST_TOKEN_CONFIG") == "1" { t.Skip("Skipping Token configuration, Organization CRUD requires Basic SecureData") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() orgs := apiClient.ListOrganizations(service.NewOrganizationFilter(), true) assert.Equal(t, len(orgs), 1) @@ -41,10 +42,10 @@ func TestOrganizationUserMembership(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - if os.Getenv("TEST_TOKEN_CONFIG") == "1" { + if os.Getenv(test_tooling.EnableTokenTestsEnv) == "1" { t.Skip("Skipping Token configuration, Organization CRUD requires Basic SecureData") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() //Create Orgs in case they aren't already present. apiClient.UploadOrganizations(service.NewOrganizationFilter()) @@ -57,7 +58,7 @@ func TestOrganizationUserMembership(t *testing.T) { apiClient.UploadUsers(service.NewUserFilter("")) // get users users := apiClient.ListUsers(service.NewUserFilter("")) - assert.Equal(t, len(users), 2) + assert.Equal(t, len(users), 3) var orgUser *models.UserSearchHitDTO for _, u := range users { if u.Login == "tux" { @@ -94,10 +95,10 @@ func TestOrganizationProperties(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - if os.Getenv("TEST_TOKEN_CONFIG") == "1" { + if os.Getenv(test_tooling.EnableTokenTestsEnv) == "1" { t.Skip("Skipping Token configuration, Organization CRUD requires Basic SecureData") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() apiClient.UploadDashboards(service.NewDashboardFilter("", "", "")) defer apiClient.DeleteAllDashboards(service.NewDashboardFilter("", "", "")) diff --git a/test/team_integration_test.go b/test/team_integration_test.go index 4d7ddb0f..7c21afb8 100644 --- a/test/team_integration_test.go +++ b/test/team_integration_test.go @@ -2,6 +2,7 @@ package test import ( "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling" "github.com/grafana/grafana-openapi-client-go/models" "golang.org/x/exp/maps" "log/slog" @@ -19,12 +20,12 @@ func TestTeamCRUD(t *testing.T) { t.Skip("Skipping Token configuration, Team and User CRUD requires Basic SecureData") } filter := service.NewTeamFilter("") - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() slog.Info("Exporting current user list") apiClient.UploadUsers(service.NewUserFilter("")) users := apiClient.ListUsers(service.NewUserFilter("")) - assert.Equal(t, len(users), 2) + assert.Equal(t, len(users), 3) slog.Info("Exporting all teams") apiClient.UploadTeams(filter) slog.Info("Listing all Teams") diff --git a/test/users_integration_test.go b/test/users_integration_test.go index 021d0b6c..a09658e9 100644 --- a/test/users_integration_test.go +++ b/test/users_integration_test.go @@ -2,6 +2,8 @@ package test import ( "github.com/esnet/gdg/internal/service" + "github.com/esnet/gdg/pkg/test_tooling" + "github.com/grafana/grafana-openapi-client-go/models" "os" "testing" @@ -9,13 +11,10 @@ import ( ) func TestUsers(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - if os.Getenv("TEST_TOKEN_CONFIG") == "1" { + if os.Getenv(test_tooling.EnableTokenTestsEnv) == "1" { t.Skip("Skipping Token configuration, Team and User CRUD requires Basic SecureData") } - apiClient, _, cleanup := initTest(t, nil) + apiClient, _, _, cleanup := test_tooling.InitTest(t, nil, false) defer cleanup() apiClient.DeleteAllUsers(service.NewUserFilter("")) //clear any previous state users := apiClient.ListUsers(service.NewUserFilter("")) @@ -24,5 +23,19 @@ func TestUsers(t *testing.T) { assert.Equal(t, adminUser.ID, int64(1)) assert.Equal(t, adminUser.Login, "admin") assert.Equal(t, adminUser.IsAdmin, true) + newUsers := apiClient.UploadUsers(service.NewUserFilter("")) + assert.Equal(t, len(newUsers), 2) + users = apiClient.ListUsers(service.NewUserFilter("")) + assert.Equal(t, len(users), 3) + var user *models.UserSearchHitDTO + for ndx, userItem := range users { + if userItem.Name == "supertux" { + user = users[ndx] + } + } + assert.NotNil(t, user) + assert.Equal(t, user.Login, "tux") + assert.Equal(t, user.Email, "s@s.com") + assert.Equal(t, user.LastSeenAtAge, "10 years") } diff --git a/website/content/docs/releases/gdg_0.5.md b/website/content/docs/releases/gdg_0.5.md index 06931069..6ba25754 100644 --- a/website/content/docs/releases/gdg_0.5.md +++ b/website/content/docs/releases/gdg_0.5.md @@ -10,7 +10,7 @@ toc: true ## Release Notes for v0.5.2 ### Changes -- [#229](https://github.com/esnet/gdg/issues/229) Datasource auth has been moved to a file based configuration under secure/. This allows for any number of secure values to be passed in. Using the wizard for initial config is recommended, or see test data for some examples. + - [#229](https://github.com/esnet/gdg/issues/229) Datasource auth has been moved to a file based configuration under secure/. This allows for any number of secure values to be passed in. Using the wizard for initial config is recommended, or see test data for some examples. - [#168](https://github.com/esnet/gdg/issues/168) Introduced a new tool called gdg-generate which allows for templating of dashboards using go.tmpl syntax. - gdg context has been moved under tools. ie. `gdg tools ctx` instead of `gdg ctx` - [#221](https://github.com/esnet/gdg/issues/221) Version check no longer requires a valid configuration diff --git a/website/content/docs/releases/gdg_0.6.md b/website/content/docs/releases/gdg_0.6.md index 98fb840d..539bc929 100644 --- a/website/content/docs/releases/gdg_0.6.md +++ b/website/content/docs/releases/gdg_0.6.md @@ -8,6 +8,27 @@ weight: 197 toc: true --- +## Release Notes for v0.6.1 + +Release Date: TBD + +### Changes + - Enterprise config flag removed, future versions will programmatically determining version of grafana. + - [#283](https://github.com/esnet/gdg/issues/283) Fixing small bug with library connections + - [#288](https://github.com/esnet/gdg/pull/288) Enterprise: Connection permission will require min. v10.2.3 + +### Bug/Security Fixes + - [#268](https://github.com/esnet/gdg/issues/268) Fixing some bad URLs in release + - [#270](https://github.com/esnet/gdg/issues/270) Fixing cli docs for deletingUserFromOrg, performance tweak to org upload. + - dependabot Bump github.com/docker/docker from 25.0.0+incompatible to 25.0.6+incompatible. + - [#285](https://github.com/esnet/gdg/issues/285) Fixing Security issue + +### Developer Changes + - Upgraded to latest grafana openapi client. + - [#269](https://github.com/esnet/gdg/issues/269) Adding a google analytics tracking on the gdg website. + + + ## Release Notes for v0.6.0 **Release Date: 03/11/2024** diff --git a/website/content/docs/usage_guide/enterprise_guide.md b/website/content/docs/usage_guide/enterprise_guide.md index 6eb6f1f2..501e2776 100644 --- a/website/content/docs/usage_guide/enterprise_guide.md +++ b/website/content/docs/usage_guide/enterprise_guide.md @@ -6,15 +6,15 @@ The features listed below are for the enterprise edition of Grafana only. They In order to use these features you need. -1. Update your context to enable enterprise features. Simply add the following flag to your context. +1. Have a running Enterprise version of grafana, I'll defer to the grafana community on instructions on how to set this up. -`enterprise_support: true` +For a docker setup, you need to set: -2. Have a running Enterprise version of grafana, I'll defer to the grafana community on instructions on how to set this up. +`GF_ENTERPRISE_LICENSE_TEXT='jwt token value'` ### Connections Permissions -Note: Available with +v0.4.6. All of these commands are a subset of the connection command. +Note: Available with +v0.4.6. All of these commands are a subset of the connection command. Requires grafana version: +v10.2.3 All commands can use `permission` or `p` to manage connection permissions. @@ -29,12 +29,32 @@ You can additionally filter by connection slug in order to only operate on a sin `./bin/gdg c permission list --connection my-elastic-connection ` + +{{< details "Permission Listing" >}} +``` +┌────┬───────────┬───────────────────┬───────────────┬─────────────────────────────────┬────────────────────────────────┬──────────────────────────────────────────────────────────────┐ +│ ID │ UID │ NAME │ SLUG │ TYPE │ DEFAULT │ URL │ +├────┼───────────┼───────────────────┼───────────────┼─────────────────────────────────┼────────────────────────────────┼──────────────────────────────────────────────────────────────┤ +│ 1 │ uL86Byf4k │ Google Sheets │ google-sheets │ grafana-googlesheets-datasource │ false │ http://localhost:3000/connections/datasources/edit/uL86Byf4k │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Admin │ User │ user:admin │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Edit │ User │ user:tux │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Query │ User │ user:sa-1-test-service-account │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Query │ Team │ team:engineers │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Admin │ BuiltinRole │ builtInRole:Admin │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Viewer │ │ +│ 1 │ uL86Byf4k │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Editor │ │ +│ 3 │ rg9qPqP7z │ netsage │ netsage │ elasticsearch │ true │ http://localhost:3000/connections/datasources/edit/rg9qPqP7z │ +│ 3 │ rg9qPqP7z │ PERMISSION--> │ Admin │ User │ user:admin │ │ +│ 3 │ rg9qPqP7z │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Editor │ │ +│ 3 │ rg9qPqP7z │ PERMISSION--> │ Admin │ BuiltinRole │ builtInRole:Admin │ │ +│ 3 │ rg9qPqP7z │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Viewer │ │ +│ 2 │ i6uqEqE7k │ Netsage TSDS │ netsage-tsds │ globalnoc-tsds-datasource │ false │ http://localhost:3000/connections/datasources/edit/i6uqEqE7k │ +│ 2 │ i6uqEqE7k │ PERMISSION--> │ Admin │ User │ user:admin │ │ +│ 2 │ i6uqEqE7k │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Viewer │ │ +│ 2 │ i6uqEqE7k │ PERMISSION--> │ Query │ BuiltinRole │ builtInRole:Editor │ │ +│ 2 │ i6uqEqE7k │ PERMISSION--> │ Admin │ BuiltinRole │ builtInRole:Admin │ │ +└────┴───────────┴───────────────────┴───────────────┴─────────────────────────────────┴────────────────────────────────┴──────────────────────────────────────────────────────────────┘ ``` -┌─────┬───────────┬──────────────────────────────────────────┬───────────────────────────────────────────────┬───────────────────────┬────────────────────────────────────────────┐ -│ ID │ UID │ NAME │ SLUG │ TYPE │ DEFAULT │ URL │ -├─────┼───────────┼──────────────────────────────────────────┼──────────────────────────┼────────────────────┼───────────────────────┼────────────────────────────────────────────┤ -│ 712 │ t5xBsTQ4k │ My Elastic Connection │ my-elastic-connection │ elasticsearch │ false │ http://localhost:3000//datasource/edit/712 │ -│ 712 │ t5xBsTQ4k │ PERMISSION--> │ Edit │ │ sa-gdg-authscope-test │ │ -│ 712 │ t5xBsTQ4k │ PERMISSION--> │ Query │ │ authscope_team_arm │ │ -└─────┴───────────┴──────────────────────────────────────────┴──────────────────────────┴────────────────────┴───────────────────────┴────────────────────────────────────────────┘ -```` +{{< /details >}} + + diff --git a/website/hugo_stats.json b/website/hugo_stats.json index 4da36fd0..981ebd53 100644 --- a/website/hugo_stats.json +++ b/website/hugo_stats.json @@ -256,6 +256,7 @@ "bug-fixes", "bug-fixes-1", "bug-fixes-2", + "bugsecurity-fixes", "buildingrunning-gdg", "buttonColorMode", "can-i-use-gdg-to-backup-grafana", @@ -341,6 +342,7 @@ "release-notes-for-v051", "release-notes-for-v052", "release-notes-for-v060", + "release-notes-for-v061", "removing-related-components", "running-tests", "search-form",