diff --git a/.gitignore b/.gitignore index 0c51a726..976d494f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ *.orig *.swp .idea + +/resources diff --git a/.golangci.toml b/.golangci.toml new file mode 100644 index 00000000..c2976a86 --- /dev/null +++ b/.golangci.toml @@ -0,0 +1,24 @@ +[linters] +disable-all = true +enable = [ + "dogsled", + "errcheck", + "exportloopref", + "goconst", + "gocritic", + "gocyclo", + "goimports", + "goprintffuncname", + "gosimple", + "govet", + "ineffassign", + "misspell", + "nakedret", + "rowserrcheck", + "staticcheck", + "stylecheck", + "typecheck", + "unconvert", + "unused", + "whitespace" +] diff --git a/Makefile b/Makefile index f2776c9d..9c1be1bd 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,11 @@ GOX := $(BIN_DIR)/gox DOCKER_COMPOSE := docker compose -f ./test-docker-compose/docker-compose.yml lint: - test -z $$(gofmt -s -l cmd/ pkg/) - go vet ./... + docker run \ + --rm \ + --volume "$(shell pwd):/src" \ + --workdir "/src" \ + golangci/golangci-lint:v1.57 golangci-lint run ./... -v run-test-image-locally: test-clean $(DOCKER_COMPOSE) up --force-recreate --detach --remove-orphans --wait diff --git a/cmd/grr/workflow.go b/cmd/grr/workflow.go index cbbbe524..3e520583 100644 --- a/cmd/grr/workflow.go +++ b/cmd/grr/workflow.go @@ -12,7 +12,7 @@ import ( "github.com/grafana/grizzly/pkg/grizzly/notifier" "github.com/hashicorp/go-multierror" log "github.com/sirupsen/logrus" - "golang.org/x/crypto/ssh/terminal" + terminal "golang.org/x/term" ) const generalFolderUID = "general" @@ -508,7 +508,9 @@ func configCmd() *cli.Command { func initialiseCmd(cmd *cli.Command, opts *Opts) *cli.Command { // Keep the old flags for backwards compatibility cmd.Flags().BoolVarP(&opts.Directory, "directory", "d", false, "treat resource path as a directory") - cmd.Flags().MarkDeprecated("directory", "now it is inferred from the operating system") + if err := cmd.Flags().MarkDeprecated("directory", "now it is inferred from the operating system"); err != nil { + log.Fatal(err) + } cmd.Flags().StringSliceVarP(&opts.Targets, "target", "t", nil, "resources to target") cmd.Flags().StringSliceVarP(&opts.JsonnetPaths, "jpath", "J", getDefaultJsonnetFolders(), "Specify an additional library search dir (right-most wins)") diff --git a/docs/content/configuration.md b/docs/content/configuration.md index 23f46a6d..067a0e45 100644 --- a/docs/content/configuration.md +++ b/docs/content/configuration.md @@ -41,7 +41,7 @@ To interact with Grafana Cloud Prometheus (aka Mimir), use these settings: ```sh grr config set mimir.address https://mimir.example.com # URL for Mimir instance or Grafana Cloud Prometheus instance -grr config set mimir.tenant-id "myTenant" # Tenant ID for your Grafana Cloud Prometheus account +grr config set mimir.tenant-id myTenant # Tenant ID for your Grafana Cloud Prometheus account grr config set mimir.api-key abcdef12345 # Authentication token (if you are using Grafana Cloud) ``` @@ -56,8 +56,10 @@ grr config set synthetic-monitoring.token abcdef123456 # API key (must have Metr grr config set synthetic-monitoring.stack-id 123 # Grafana stack ID grr config set synthetic-monitoring.metrics-id 123 # Metrics instance ID grr config set synthetic-monitoring.logs-id 123 # Logs instance ID +grr config set synthetic-monitoring.url https://synthetic-monitoring-api.grafana.net # Synthetic Monitoring instance URL ``` Your stack ID is the number at the end of the url when you view your Grafana instance details, ie. `grafana.com/orgs/myorg/stacks/123456` would be `123456`. Your metrics and logs ID's are the `User` when you view your Prometheus or Loki instance details in Grafana Cloud. +You can find your instance URL under your Synthetic Monitoring configuration. ## Configuring Targets Grizzly supports a number of resource types (`grr providers` will list those supported). Often, however, we do not @@ -136,11 +138,11 @@ In some circumstances (e.g. when used within automated pipelines) it makes sense with environment variables as opposed to contexts. Environment variables, when set, take precedence over Grizzly contexts as described above. Below are the variables that can be used for this. -| Name | Description | Required | Default | -| --- | --- | --- | --- | -| `GRAFANA_URL` | Fully qualified domain name of your Grafana instance. | true | - | -| `GRAFANA_USER` | Basic auth username if applicable. | false | `api_key` | -| `GRAFANA_TOKEN` | Basic auth password or API token. | false | - | +| Name | Description | Required | Default | +|-----------------|-------------------------------------------------------|----------|-----------| +| `GRAFANA_URL` | Fully qualified domain name of your Grafana instance. | true | - | +| `GRAFANA_USER` | Basic auth username if applicable. | false | `api_key` | +| `GRAFANA_TOKEN` | Basic auth password or API token. | false | - | See Grafana's [Authentication API docs](https://grafana.com/docs/grafana/latest/http_api/auth/) for more info. @@ -148,25 +150,35 @@ docs](https://grafana.com/docs/grafana/latest/http_api/auth/) for more info. ## Grafana Cloud Prometheus To interact with Grafana Cloud Prometheus, you must have these environment variables set: +<<<<<<< HEAD | Name | Description | Required | |--------------------| --- |----------| | `MIMIR_ADDRESS` | URL for Grafana Cloud Prometheus instance | true | | `MIMIR_TENANT_ID` | Tenant ID for your Grafana Cloud Prometheus account | true | | `MIMIR_API_KEY` | Authentication token/api key | false | +======= +| Name | Description | Required | +|--------------------|-----------------------------------------------------|----------| +| `CORTEX_ADDRESS` | URL for Grafana Cloud Prometheus instance | true | +| `CORTEX_TENANT_ID` | Tenant ID for your Grafana Cloud Prometheus account | true | +| `CORTEX_API_KEY` | Authentication token/api key | true | +>>>>>>> f7c171e48e49970e97686a84c2d505c742b5bc9a Note, this will also work with other Mimir installations, alongside Grafana Cloud Prometheus. ## Grafana Synthetic Monitoring To interact with Grafana Synthetic Monitoring, you must have these environment variable set: -| Name | Description | Required | -| --- | --- | --- | -| `GRAFANA_SM_TOKEN` | Authentication token/api key (must have MetricsPublisher permissions) | true | -| `GRAFANA_SM_STACK_ID` | Grafana instance/stack ID | true | -| `GRAFANA_SM_LOGS_ID` | Logs instance ID | true | -| `GRAFANA_SM_METRICS_ID` | Metrics instance ID | true | +| Name | Description | Required | +|-------------------------|-----------------------------------------------------------------------|----------| +| `GRAFANA_SM_TOKEN` | Authentication token/api key (must have MetricsPublisher permissions) | true | +| `GRAFANA_SM_STACK_ID` | Grafana instance/stack ID | true | +| `GRAFANA_SM_LOGS_ID` | Logs instance ID | true | +| `GRAFANA_SM_METRICS_ID` | Metrics instance ID | true | +| `GRAFANA_SM_URL` | Synthetic Monitoring instance URL | true | Your stack ID is the number at the end of the url when you view your Grafana instance details, ie. `grafana.com/orgs/myorg/stacks/123456` would be `123456`. Your metrics and logs ID's are the `User` when you view your Prometheus or Loki instance details in Grafana Cloud. +You can find your instance URL under your Synthetic Monitoring configuration. # Grizzly configuration file To get the path of the config file: diff --git a/go.mod b/go.mod index 43f46c9a..55449cc0 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect diff --git a/go.sum b/go.sum index b25098ee..ab7520ef 100644 --- a/go.sum +++ b/go.sum @@ -195,8 +195,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 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= diff --git a/integration/folder_test.go b/integration/folder_test.go index ddaaeb7f..4373b3bc 100644 --- a/integration/folder_test.go +++ b/integration/folder_test.go @@ -83,7 +83,7 @@ func TestFolders(t *testing.T) { t.Run("get remote folder - not found", func(t *testing.T) { _, err := handler.GetByUID("dummy") - require.ErrorContains(t, err, "Couldn't fetch folder 'dummy' from remote: not found") + require.ErrorContains(t, err, "couldn't fetch folder 'dummy' from remote: not found") }) t.Run("get folders list", func(t *testing.T) { diff --git a/integration/pull_test.go b/integration/pull_test.go index 19d6b126..18fcd79d 100644 --- a/integration/pull_test.go +++ b/integration/pull_test.go @@ -29,7 +29,6 @@ func TestPull(t *testing.T) { assert.FileExists(t, filepath.Join(pullDir, "dashboards", "abcdefghi", "dashboard-ReciqtgGk.yaml")) assert.DirExists(t, filepath.Join(pullDir, "datasources")) assert.DirExists(t, filepath.Join(pullDir, "folders")) - }, }) }) diff --git a/pkg/config/config.go b/pkg/config/config.go index d97073c7..cfc7c6b5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -16,8 +16,7 @@ import ( ) const ( - API_VERSION = "v1alpha1" - CURRENT_CONTEXT = "current-context" + CurrentContextSetting = "current-context" ) func Initialise() { @@ -38,6 +37,7 @@ func override(v *viper.Viper) { "synthetic-monitoring.stack-id": "GRAFANA_SM_STACK_ID", "synthetic-monitoring.logs-id": "GRAFANA_SM_LOGS_ID", "synthetic-monitoring.metrics-id": "GRAFANA_SM_METRICS_ID", + "synthetic-monitoring.url": "GRAFANA_SM_URL", "mimir.address": "MIMIR_ADDRESS", "mimir.tenant-id": "MIMIR_TENANT_ID", @@ -89,7 +89,7 @@ func Mock(values map[string]interface{}) { } func Import() error { - name := viper.GetString(CURRENT_CONTEXT) + name := viper.GetString(CurrentContextSetting) if name == "" { NewConfig() return Import() @@ -109,14 +109,16 @@ func Import() error { func NewConfig() { viper.Set("apiVersion", "v1alpha1") - viper.Set(CURRENT_CONTEXT, "default") + viper.Set(CurrentContextSetting, "default") viper.Set("contexts.default.name", "default") } func GetContexts() error { contexts := map[string]interface{}{} - currentContext := viper.GetString(CURRENT_CONTEXT) - viper.UnmarshalKey("contexts", &contexts) + currentContext := viper.GetString(CurrentContextSetting) + if err := viper.UnmarshalKey("contexts", &contexts); err != nil { + return err + } keys := make([]string, 0, len(contexts)) for k := range contexts { keys = append(keys, k) @@ -134,10 +136,12 @@ func GetContexts() error { func UseContext(context string) error { contexts := map[string]interface{}{} - viper.UnmarshalKey("contexts", &contexts) + if err := viper.UnmarshalKey("contexts", &contexts); err != nil { + return err + } for k := range contexts { if k == context { - viper.Set(CURRENT_CONTEXT, context) + viper.Set(CurrentContextSetting, context) return Write() } } @@ -145,7 +149,7 @@ func UseContext(context string) error { } func CurrentContext() (*Context, error) { - name := viper.GetString(CURRENT_CONTEXT) + name := viper.GetString(CurrentContextSetting) if name == "" { NewConfig() return CurrentContext() @@ -157,7 +161,9 @@ func CurrentContext() (*Context, error) { } override(ctx) var context Context - ctx.Unmarshal(&context) + if err := ctx.Unmarshal(&context); err != nil { + return nil, err + } context.Name = name return &context, nil } @@ -166,6 +172,8 @@ var acceptableKeys = map[string]string{ "grafana.url": "string", "grafana.token": "string", "grafana.user": "string", + "grafana.insecure-skip-verify": "bool", + "grafana.tls-host": "string", "mimir.address": "string", "mimir.tenant-id": "string", "mimir.api-key": "string", @@ -173,13 +181,14 @@ var acceptableKeys = map[string]string{ "synthetic-monitoring.stack-id": "int", "synthetic-monitoring.metrics-id": "int", "synthetic-monitoring.logs-id": "int", + "synthetic-monitoring.url": "string", "targets": "[]string", "output-format": "string", "only-spec": "bool", } func Get(path, outputFormat string) (string, error) { - ctx := viper.GetString(CURRENT_CONTEXT) + ctx := viper.GetString(CurrentContextSetting) fullPath := fmt.Sprintf("contexts.%s", ctx) if path != "" { fullPath = fmt.Sprintf("%s.%s", fullPath, path) @@ -202,7 +211,7 @@ func Get(path, outputFormat string) (string, error) { func Set(path string, value string) error { for key, typ := range acceptableKeys { if path == key { - ctx := viper.GetString(CURRENT_CONTEXT) + ctx := viper.GetString(CurrentContextSetting) fullPath := fmt.Sprintf("contexts.%s.%s", ctx, path) var val any switch typ { @@ -241,7 +250,7 @@ func Unset(path string) error { return fmt.Errorf("%s is not a valid path", path) } - ctx := viper.GetString(CURRENT_CONTEXT) + ctx := viper.GetString(CurrentContextSetting) fullPath := fmt.Sprintf("contexts.%s.%s", ctx, path) if !viper.InConfig(fullPath) { @@ -272,7 +281,7 @@ func deleteValue(settings map[string]any, deleteKey string, iteratorKeys ...stri } func CreateContext(name string) error { - viper.Set(CURRENT_CONTEXT, name) + viper.Set(CurrentContextSetting, name) viper.Set(fmt.Sprintf("contexts.%s.name", name), name) return Write() } diff --git a/pkg/config/model.go b/pkg/config/model.go index 8e1393fb..7dc2bcfa 100644 --- a/pkg/config/model.go +++ b/pkg/config/model.go @@ -1,15 +1,17 @@ package config type GrafanaConfig struct { - URL string `yaml:"url" mapstructure:"url"` - User string `yaml:"user" mapstructure:"user"` - Token string `yaml:"token" mapstructure:"token"` + URL string `yaml:"url" mapstructure:"url"` + User string `yaml:"user" mapstructure:"user"` + Token string `yaml:"token" mapstructure:"token"` + InsecureSkipVerify bool `yaml:"insecure-skip-verify" mapstructure:"insecure-skip-verify"` + TLSHost string `yaml:"tls-host" mapstructure:"tls-host"` } type MimirConfig struct { Address string `yaml:"address" mapstructure:"address"` TenantID string `yaml:"tenant-id" mapstructure:"tenant-id"` - ApiKey string `yaml:"api-key" mapstructure:"api-key"` + APIKey string `yaml:"api-key" mapstructure:"api-key"` } type SyntheticMonitoringConfig struct { @@ -17,6 +19,7 @@ type SyntheticMonitoringConfig struct { StackID int64 `yaml:"stack-id" mapstructure:"stack-id"` LogsID int64 `yaml:"logs-id" mapstructure:"logs-id"` MetricsID int64 `yaml:"metrics-id" mapstructure:"metrics-id"` + URL string `yaml:"url" mapstructure:"url"` } type Context struct { diff --git a/pkg/grafana/alertgroup-handler.go b/pkg/grafana/alertgroup-handler.go index 33fa6efa..bef46895 100644 --- a/pkg/grafana/alertgroup-handler.go +++ b/pkg/grafana/alertgroup-handler.go @@ -59,8 +59,8 @@ func (h *AlertRuleGroupHandler) GetSpecUID(resource grizzly.Resource) (string, e } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *AlertRuleGroupHandler) GetByUID(UID string) (*grizzly.Resource, error) { - return h.getRemoteAlertRuleGroup(UID) +func (h *AlertRuleGroupHandler) GetByUID(uid string) (*grizzly.Resource, error) { + return h.getRemoteAlertRuleGroup(uid) } // GetRemote retrieves a alertRuleGroup as a Resource @@ -146,7 +146,6 @@ func (h *AlertRuleGroupHandler) createAlertRule(rule *models.ProvisionedAlertRul return err } - stringtrue := "true" params := provisioning.NewPostAlertRuleParams().WithBody(rule).WithXDisableProvenance(&stringtrue) _, err = client.Provisioning.PostAlertRule(params, nil) return err @@ -158,7 +157,9 @@ func (h *AlertRuleGroupHandler) createAlertRuleGroup(resource grizzly.Resource) return err } var group models.AlertRuleGroup - err = json.Unmarshal(data, &group) + if err := json.Unmarshal(data, &group); err != nil { + return err + } for _, r := range group.Rules { if err := h.createAlertRule(r); err != nil { @@ -171,7 +172,6 @@ func (h *AlertRuleGroupHandler) createAlertRuleGroup(resource grizzly.Resource) return err } - stringtrue := "true" params := provisioning.NewPutAlertRuleGroupParams(). WithBody(&group). WithGroup(group.Title). @@ -199,7 +199,6 @@ func (h *AlertRuleGroupHandler) updateAlertRule(rule *models.ProvisionedAlertRul return fmt.Errorf("fetching alert rule: %w", err) } } else { - stringtrue := "true" params := provisioning.NewPostAlertRuleParams(). WithBody(rule). WithXDisableProvenance(&stringtrue) @@ -207,7 +206,6 @@ func (h *AlertRuleGroupHandler) updateAlertRule(rule *models.ProvisionedAlertRul return err } - stringtrue := "true" params := provisioning.NewPutAlertRuleParams(). WithUID(rule.UID). WithBody(rule). @@ -272,7 +270,6 @@ func (h *AlertRuleGroupHandler) putAlertRuleGroup(existing, resource grizzly.Res return err } - stringtrue := "true" params := provisioning.NewPutAlertRuleGroupParams(). WithBody(group). WithGroup(group.Title). diff --git a/pkg/grafana/contactpoint-handler.go b/pkg/grafana/contactpoint-handler.go index 5ec09537..7d27c3ef 100644 --- a/pkg/grafana/contactpoint-handler.go +++ b/pkg/grafana/contactpoint-handler.go @@ -57,8 +57,8 @@ func (h *AlertContactPointHandler) GetSpecUID(resource grizzly.Resource) (string } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *AlertContactPointHandler) GetByUID(UID string) (*grizzly.Resource, error) { - return h.getRemoteContactPoint(UID) +func (h *AlertContactPointHandler) GetByUID(uid string) (*grizzly.Resource, error) { + return h.getRemoteContactPoint(uid) } // GetRemote retrieves a contactPoint as a Resource @@ -151,7 +151,6 @@ func (h *AlertContactPointHandler) postContactPoint(resource grizzly.Resource) e if err != nil { return err } - stringtrue := "true" params := provisioning.NewPostContactpointsParams(). WithBody(&contactPoint). WithXDisableProvenance(&stringtrue) @@ -176,7 +175,6 @@ func (h *AlertContactPointHandler) putContactPoint(resource grizzly.Resource) er if err != nil { return err } - stringtrue := "true" params := provisioning.NewPutContactpointParams(). WithUID(resource.Name()). WithBody(&modelContactPoint). diff --git a/pkg/grafana/dashboard-handler.go b/pkg/grafana/dashboard-handler.go index 5db2959f..f9229fdb 100644 --- a/pkg/grafana/dashboard-handler.go +++ b/pkg/grafana/dashboard-handler.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "os" + "strings" "github.com/go-chi/chi" "github.com/grafana/grafana-openapi-client-go/client/dashboards" @@ -17,7 +18,7 @@ import ( ) // Moved from utils.go -const generalFolderId = 0 +const generalFolderID = 0 const generalFolderUID = "general" // DashboardHandler is a Grizzly Handler for Grafana dashboards @@ -77,10 +78,10 @@ func (h *DashboardHandler) GetSpecUID(resource grizzly.Resource) (string, error) } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *DashboardHandler) GetByUID(UID string) (*grizzly.Resource, error) { - resource, err := h.getRemoteDashboard(UID) +func (h *DashboardHandler) GetByUID(uid string) (*grizzly.Resource, error) { + resource, err := h.getRemoteDashboard(uid) if err != nil { - return nil, fmt.Errorf("Error retrieving dashboard %s: %w", UID, err) + return nil, fmt.Errorf("Error retrieving dashboard %s: %w", uid, err) } return resource, nil } @@ -152,8 +153,8 @@ func (h *DashboardHandler) getRemoteDashboard(uid string) (*grizzly.Resource, er if err != nil { return nil, err } - folderUid := extractFolderUID(client, *dashboard) - resource.SetMetadata("folder", folderUid) + folderUID := extractFolderUID(client, *dashboard) + resource.SetMetadata("folder", folderUID) return &resource, nil } @@ -192,7 +193,7 @@ func (h *DashboardHandler) getRemoteDashboardList() ([]string, error) { func (h *DashboardHandler) postDashboard(resource grizzly.Resource) error { folderUID := resource.GetMetadata("folder") var folderID int64 - if !(folderUID == "General" || folderUID == "general") { + if !(folderUID == DefaultFolder || folderUID == strings.ToLower(DefaultFolder)) { folderHandler := NewFolderHandler(h.Provider) folder, err := folderHandler.getRemoteFolder(folderUID) if err != nil { @@ -204,7 +205,7 @@ func (h *DashboardHandler) postDashboard(resource grizzly.Resource) error { } folderID = int64(folder.GetSpecValue("id").(float64)) } else { - folderID = generalFolderId + folderID = generalFolderID } body := models.SaveDashboardCommand{ @@ -259,22 +260,22 @@ func (h *DashboardHandler) GetProxyEndpoints(p grizzly.Server) []grizzly.ProxyEn return []grizzly.ProxyEndpoint{ { Method: "GET", - Url: "/d/{uid}/{slug}", + URL: "/d/{uid}/{slug}", Handler: h.resourceFromQueryParameterMiddleware(p, "grizzly_from_file", h.RootDashboardPageHandler(p)), }, { Method: "GET", - Url: "/api/dashboards/uid/{uid}", + URL: "/api/dashboards/uid/{uid}", Handler: h.DashboardJSONGetHandler(p), }, { Method: "POST", - Url: "/api/dashboards/db", + URL: "/api/dashboards/db", Handler: h.DashboardJSONPostHandler(p), }, { Method: "POST", - Url: "/api/dashboards/db/", + URL: "/api/dashboards/db/", Handler: h.DashboardJSONPostHandler(p), }, } @@ -314,7 +315,7 @@ func (h *DashboardHandler) RootDashboardPageHandler(p grizzly.Server) http.Handl if err == nil { body, _ := io.ReadAll(resp.Body) - w.Write(body) + writeOrLog(w, body) return } @@ -363,7 +364,7 @@ func (h *DashboardHandler) DashboardJSONGetHandler(p grizzly.Server) http.Handle } out, _ := json.Marshal(wrapper) - w.Write(out) + writeOrLog(w, out) } } @@ -426,7 +427,7 @@ func (h *DashboardHandler) DashboardJSONPostHandler(p grizzly.Server) http.Handl "version": 1, } body, _ := json.Marshal(jout) - w.Write(body) + writeOrLog(w, body) } } diff --git a/pkg/grafana/datasource-handler.go b/pkg/grafana/datasource-handler.go index 18153db3..f697f00e 100644 --- a/pkg/grafana/datasource-handler.go +++ b/pkg/grafana/datasource-handler.go @@ -72,8 +72,8 @@ func (h *DatasourceHandler) GetSpecUID(resource grizzly.Resource) (string, error } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *DatasourceHandler) GetByUID(UID string) (*grizzly.Resource, error) { - return h.getRemoteDatasource(UID) +func (h *DatasourceHandler) GetByUID(uid string) (*grizzly.Resource, error) { + return h.getRemoteDatasource(uid) } // GetRemote retrieves a datasource as a Resource diff --git a/pkg/grafana/errors.go b/pkg/grafana/errors.go index 7c273fb3..7eb5d2c7 100644 --- a/pkg/grafana/errors.go +++ b/pkg/grafana/errors.go @@ -2,7 +2,10 @@ package grafana import ( "fmt" + "net/http" "strings" + + log "github.com/sirupsen/logrus" ) // ErrUidsMissing reports UIDs are missing for Dashboards @@ -17,3 +20,9 @@ type APIResponse interface { Error() string String() string } + +func writeOrLog(w http.ResponseWriter, content []byte) { + if _, err := w.Write(content); err != nil { + log.Errorf("error writing response: %v", err) + } +} diff --git a/pkg/grafana/folder-handler.go b/pkg/grafana/folder-handler.go index 39c96588..02fa44e9 100644 --- a/pkg/grafana/folder-handler.go +++ b/pkg/grafana/folder-handler.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" gclient "github.com/grafana/grafana-openapi-client-go/client" "github.com/grafana/grafana-openapi-client-go/client/folders" @@ -12,6 +13,8 @@ import ( "github.com/grafana/grizzly/pkg/grizzly" ) +const DefaultFolder = "General" + // FolderHandler is a Grizzly Handler for Grafana dashboard folders type FolderHandler struct { grizzly.BaseHandler @@ -115,10 +118,10 @@ func (h *FolderHandler) Sort(resources grizzly.Resources) grizzly.Resources { } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *FolderHandler) GetByUID(UID string) (*grizzly.Resource, error) { - resource, err := h.getRemoteFolder(UID) +func (h *FolderHandler) GetByUID(uid string) (*grizzly.Resource, error) { + resource, err := h.getRemoteFolder(uid) if err != nil { - return nil, fmt.Errorf("Error retrieving dashboard folder %s: %w", UID, err) + return nil, fmt.Errorf("Error retrieving dashboard folder %s: %w", uid, err) } return resource, nil @@ -147,14 +150,14 @@ func (h *FolderHandler) Update(existing, resource grizzly.Resource) error { // getRemoteFolder retrieves a folder object from Grafana func (h *FolderHandler) getRemoteFolder(uid string) (*grizzly.Resource, error) { if uid == "" { - return nil, fmt.Errorf("No folder UID provided") + return nil, fmt.Errorf("no folder UID provided") } var folder *models.Folder - if uid == "General" || uid == "general" { + if uid == DefaultFolder || uid == strings.ToLower(DefaultFolder) { folder = &models.Folder{ ID: 0, UID: uid, - Title: "General", + Title: DefaultFolder, // URL: ?? } } else { @@ -168,7 +171,7 @@ func (h *FolderHandler) getRemoteFolder(uid string) (*grizzly.Resource, error) { var gErrNotFound *folders.GetFolderByUIDNotFound var gErrForbidden *folders.GetFolderByUIDForbidden if errors.As(err, &gErrNotFound) || errors.As(err, &gErrForbidden) { - return nil, fmt.Errorf("Couldn't fetch folder '%s' from remote: %w", uid, grizzly.ErrNotFound) + return nil, fmt.Errorf("couldn't fetch folder '%s' from remote: %w", uid, grizzly.ErrNotFound) } return nil, err } @@ -193,7 +196,7 @@ func (h *FolderHandler) getRemoteFolderList() ([]string, error) { limit = int64(1000) page int64 = 0 uids []string - folderType string = "dash-folder" + folderType = "dash-folder" ) params := search.NewSearchParams().WithLimit(&limit) @@ -223,7 +226,7 @@ func (h *FolderHandler) getRemoteFolderList() ([]string, error) { func (h *FolderHandler) postFolder(resource grizzly.Resource) error { name := resource.Name() - if name == "General" || name == "general" { + if name == DefaultFolder || name == strings.ToLower(DefaultFolder) { return nil } @@ -285,7 +288,7 @@ func (h *FolderHandler) putFolder(resource grizzly.Resource) error { return err } -var getFolderById = func(client *gclient.GrafanaHTTPAPI, folderId int64) (*models.Folder, error) { +var getFolderByID = func(client *gclient.GrafanaHTTPAPI, folderId int64) (*models.Folder, error) { folderOk, err := client.Folders.GetFolderByID(folderId) if err != nil { return nil, err diff --git a/pkg/grafana/library-element-handler.go b/pkg/grafana/library-element-handler.go index e1c4fc17..d5f587dc 100644 --- a/pkg/grafana/library-element-handler.go +++ b/pkg/grafana/library-element-handler.go @@ -86,10 +86,10 @@ func (h *LibraryElementHandler) GetSpecUID(resource grizzly.Resource) (string, e } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *LibraryElementHandler) GetByUID(UID string) (*grizzly.Resource, error) { - resource, err := h.getRemoteLibraryElement(UID) +func (h *LibraryElementHandler) GetByUID(uid string) (*grizzly.Resource, error) { + resource, err := h.getRemoteLibraryElement(uid) if err != nil { - return nil, fmt.Errorf("Error retrieving library element %s: %w", UID, err) + return nil, fmt.Errorf("Error retrieving library element %s: %w", uid, err) } return resource, nil diff --git a/pkg/grafana/notificationpolicy-handler.go b/pkg/grafana/notificationpolicy-handler.go index fa4e5477..f9c5571c 100644 --- a/pkg/grafana/notificationpolicy-handler.go +++ b/pkg/grafana/notificationpolicy-handler.go @@ -47,7 +47,7 @@ func (h *AlertNotificationPolicyHandler) GetSpecUID(resource grizzly.Resource) ( } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *AlertNotificationPolicyHandler) GetByUID(UID string) (*grizzly.Resource, error) { +func (h *AlertNotificationPolicyHandler) GetByUID(uid string) (*grizzly.Resource, error) { return h.getRemoteAlertNotificationPolicy() } @@ -117,7 +117,6 @@ func (h *AlertNotificationPolicyHandler) putAlertNotificationPolicy(resource gri if err != nil { return err } - stringtrue := "true" params := provisioning.NewPutPolicyTreeParams(). WithBody(&alertNotificationPolicy). WithXDisableProvenance(&stringtrue) diff --git a/pkg/grafana/provider.go b/pkg/grafana/provider.go index f18dd0cf..8cb90b69 100644 --- a/pkg/grafana/provider.go +++ b/pkg/grafana/provider.go @@ -1,6 +1,7 @@ package grafana import ( + "crypto/tls" "encoding/base64" "fmt" "net/http/httputil" @@ -52,15 +53,22 @@ func (p *Provider) Client() (*gclient.GrafanaHTTPAPI, error) { return p.client, nil } - parsedUrl, err := url.Parse(p.config.URL) + parsedURL, err := url.Parse(p.config.URL) if err != nil { return nil, fmt.Errorf("invalid Grafana URL") } transportConfig := gclient.DefaultTransportConfig(). - WithHost(parsedUrl.Host). - WithSchemes([]string{parsedUrl.Scheme}). - WithBasePath(filepath.Join(parsedUrl.Path, "api")) + WithHost(parsedURL.Host). + WithSchemes([]string{parsedURL.Scheme}). + WithBasePath(filepath.Join(parsedURL.Path, "api")) + + if parsedURL.Scheme == "https" && p.config.InsecureSkipVerify { + transportConfig.TLSConfig = &tls.Config{ + InsecureSkipVerify: true, + ServerName: p.config.TLSHost, + } + } if p.config.Token != "" { if p.config.User != "" { diff --git a/pkg/grafana/utils.go b/pkg/grafana/utils.go index f5ff6f5f..d7120a46 100644 --- a/pkg/grafana/utils.go +++ b/pkg/grafana/utils.go @@ -8,25 +8,28 @@ import ( "github.com/grafana/grafana-openapi-client-go/models" ) -var folderURLRegex = regexp.MustCompile("/dashboards/f/([^/]+)") +var ( + stringtrue = "true" + folderURLRegex = regexp.MustCompile("/dashboards/f/([^/]+)") +) func extractFolderUID(client *gclient.GrafanaHTTPAPI, d models.DashboardFullWithMeta) string { - folderUid := d.Meta.FolderUID - if folderUid == "" { + folderUID := d.Meta.FolderUID + if folderUID == "" { urlPaths := folderURLRegex.FindStringSubmatch(d.Meta.FolderURL) if len(urlPaths) == 0 { - if d.Meta.FolderID == generalFolderId { + if d.Meta.FolderID == generalFolderID { // nolint:staticcheck return generalFolderUID } - folder, err := getFolderById(client, d.Meta.FolderID) + folder, err := getFolderByID(client, d.Meta.FolderID) // nolint:staticcheck if err != nil { return "" } return folder.UID } - folderUid = urlPaths[1] + folderUID = urlPaths[1] } - return folderUid + return folderUID } func structToMap(s interface{}) (map[string]interface{}, error) { diff --git a/pkg/grafana/utils_test.go b/pkg/grafana/utils_test.go index 63d13e5a..639f3dce 100644 --- a/pkg/grafana/utils_test.go +++ b/pkg/grafana/utils_test.go @@ -45,7 +45,7 @@ func TestExtractFolderUID(t *testing.T) { dashboard := models.DashboardFullWithMeta{ Meta: &meta, } - getFolderById = func(client *gclient.GrafanaHTTPAPI, folderId int64) (*models.Folder, error) { + getFolderByID = func(client *gclient.GrafanaHTTPAPI, folderId int64) (*models.Folder, error) { return &models.Folder{ UID: "12345", }, nil diff --git a/pkg/grizzly/formatting.go b/pkg/grizzly/formatting.go index e69eb67c..57ad3990 100644 --- a/pkg/grizzly/formatting.go +++ b/pkg/grizzly/formatting.go @@ -8,6 +8,13 @@ import ( "gopkg.in/yaml.v3" ) +const ( + formatJSON = "json" + formatYAML = "yaml" + formatWide = "wide" + formatDefault = "default" +) + func Format(registry Registry, resourcePath string, resource *Resource, format string, onlySpec bool) ([]byte, string, string, error) { var content []byte var filename string @@ -19,15 +26,15 @@ func Format(registry Registry, resourcePath string, resource *Resource, format s spec = resource.Spec() } - if format == "json" { - extension = "json" + if format == formatJSON { + extension = formatJSON j, err := json.MarshalIndent(spec, "", " ") if err != nil { return nil, "", "", err } content = j } else { - extension = "yaml" + extension = formatYAML y, err := yaml.Marshal(spec) if err != nil { return nil, "", "", err diff --git a/pkg/grizzly/handler.go b/pkg/grizzly/handler.go index 94ddf2b2..3aedcb57 100644 --- a/pkg/grizzly/handler.go +++ b/pkg/grizzly/handler.go @@ -114,7 +114,7 @@ type ListenHandler interface { type ProxyEndpoint struct { Method string - Url string + URL string Handler func(http.ResponseWriter, *http.Request) } diff --git a/pkg/grizzly/json.go b/pkg/grizzly/json.go index 59a0846a..20a55a05 100644 --- a/pkg/grizzly/json.go +++ b/pkg/grizzly/json.go @@ -35,7 +35,7 @@ func (parser *JSONParser) Parse(file string, options ParserOptions) (Resources, } source := Source{ - Format: "json", + Format: formatJSON, Path: file, Rewritable: true, } diff --git a/pkg/grizzly/parsing.go b/pkg/grizzly/parsing.go index f96bf9f0..23f4f645 100644 --- a/pkg/grizzly/parsing.go +++ b/pkg/grizzly/parsing.go @@ -288,10 +288,8 @@ func ValidateEnvelope(data any) error { s, ok := spec.(map[string]any) if !ok { errors = append(errors, "spec is not a map") - } else { - if len(s) == 0 { - errors = append(errors, "spec should not be empty") - } + } else if len(s) == 0 { + errors = append(errors, "spec should not be empty") } } diff --git a/pkg/grizzly/parsing_test.go b/pkg/grizzly/parsing_test.go index ff2960d2..af434b7a 100644 --- a/pkg/grizzly/parsing_test.go +++ b/pkg/grizzly/parsing_test.go @@ -199,7 +199,7 @@ func TestParseKindDetection(t *testing.T) { parser := grizzly.DefaultParser(registry, nil, nil) parseOpts := grizzly.ParserOptions{ DefaultResourceKind: "", - DefaultFolderUID: "General", + DefaultFolderUID: grafana.DefaultFolder, } for _, test := range tests { diff --git a/pkg/grizzly/registry.go b/pkg/grizzly/registry.go index 45b23b93..33439300 100644 --- a/pkg/grizzly/registry.go +++ b/pkg/grizzly/registry.go @@ -73,14 +73,14 @@ func (r *Registry) HandlerMatchesTarget(handler Handler, targets []string) bool } // ResourceMatchesTarget identifies whether a resource is in a target list -func (r *Registry) ResourceMatchesTarget(kind string, UID string, targets []string) bool { +func (r *Registry) ResourceMatchesTarget(kind string, uid string, targets []string) bool { if len(targets) == 0 { return true } // I mistakenly assumed 'dot' was a special character for globs, so opted for '/' as separator. // This keeps back-compat - slashKey := fmt.Sprintf("%s/%s", kind, UID) - dotKey := fmt.Sprintf("%s.%s", kind, UID) + slashKey := fmt.Sprintf("%s/%s", kind, uid) + dotKey := fmt.Sprintf("%s.%s", kind, uid) for _, target := range targets { if strings.Contains(target, ".") || strings.Contains(target, "/") { g, err := glob.Compile(target) @@ -131,7 +131,7 @@ func (r *Registry) GetProxyProvider() (*ProxyProvider, error) { if proxyProvider == nil { proxyProvider = &pp } else { - return nil, fmt.Errorf("Only one proxy provider currently supported") + return nil, fmt.Errorf("only one proxy provider currently supported") } } } diff --git a/pkg/grizzly/resources.go b/pkg/grizzly/resources.go index cfee98b8..bcca822b 100644 --- a/pkg/grizzly/resources.go +++ b/pkg/grizzly/resources.go @@ -170,7 +170,6 @@ func (r *Resource) SpecAsJSON() (string, error) { return "", err } return string(j), nil - } // YAML Gets the string representation for this resource diff --git a/pkg/grizzly/server.go b/pkg/grizzly/server.go index ee954bc2..d4e9b397 100644 --- a/pkg/grizzly/server.go +++ b/pkg/grizzly/server.go @@ -122,9 +122,9 @@ func (p *Server) Start() error { for _, endpoint := range proxyHandler.GetProxyEndpoints(*p) { switch endpoint.Method { case "GET": - r.Get(endpoint.Url, endpoint.Handler) + r.Get(endpoint.URL, endpoint.Handler) case "POST": - r.Post(endpoint.Url, endpoint.Handler) + r.Post(endpoint.URL, endpoint.Handler) default: return fmt.Errorf("unknown endpoint method %s for handler %s", endpoint.Method, handler.Kind()) } @@ -233,7 +233,9 @@ func (p *Server) blockHandler(response string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(response)) + if _, err := w.Write([]byte(response)); err != nil { + log.Errorf("error writing response: %v", err) + } } } diff --git a/pkg/grizzly/workflow.go b/pkg/grizzly/workflow.go index c9a80263..ccf12441 100644 --- a/pkg/grizzly/workflow.go +++ b/pkg/grizzly/workflow.go @@ -17,7 +17,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/pmezard/go-difflib/difflib" log "github.com/sirupsen/logrus" - "golang.org/x/crypto/ssh/terminal" + terminal "golang.org/x/term" "gopkg.in/fsnotify.v1" "gopkg.in/yaml.v3" ) @@ -25,22 +25,22 @@ import ( var interactive = terminal.IsTerminal(int(os.Stdout.Fd())) // Get retrieves a resource from a remote endpoint using its UID -func Get(registry Registry, UID string, onlySpec bool, outputFormat string) error { - log.Info("Getting ", UID) +func Get(registry Registry, uid string, onlySpec bool, outputFormat string) error { + log.Info("Getting ", uid) - count := strings.Count(UID, ".") + count := strings.Count(uid, ".") var handlerName, resourceID string - if count == 1 { - parts := strings.SplitN(UID, ".", 2) + switch count { + case 1: + parts := strings.SplitN(uid, ".", 2) handlerName = parts[0] resourceID = parts[1] - } else if count == 2 { - parts := strings.SplitN(UID, ".", 3) + case 2: + parts := strings.SplitN(uid, ".", 3) handlerName = parts[0] + "." + parts[1] resourceID = parts[2] - - } else { - return fmt.Errorf("UID must be .: %s", UID) + default: + return fmt.Errorf("UID must be .: %s", uid) } handler, err := registry.GetHandler(handlerName) @@ -124,13 +124,13 @@ func listResources(listedResources []listedResource, format string) error { var output []byte var err error switch format { - case "yaml": + case formatYAML: output, err = yaml.Marshal(listedResources) - case "json": + case formatJSON: output, err = json.MarshalIndent(listedResources, " ", "") - case "default": + case formatDefault: output, err = listDefault(listedResources) - case "wide": + case formatWide: output, err = listWide(listedResources) } if err != nil { @@ -141,7 +141,6 @@ func listResources(listedResources []listedResource, format string) error { } func listDefault(listedResources []listedResource) ([]byte, error) { - var out bytes.Buffer var f string w := tabwriter.NewWriter(&out, 0, 0, 4, ' ', 0) @@ -157,7 +156,6 @@ func listDefault(listedResources []listedResource) ([]byte, error) { } func listWide(listedResources []listedResource) ([]byte, error) { - var out bytes.Buffer var f string w := tabwriter.NewWriter(&out, 0, 0, 4, ' ', 0) diff --git a/pkg/grizzly/yaml.go b/pkg/grizzly/yaml.go index 19478c08..d378c6fd 100644 --- a/pkg/grizzly/yaml.go +++ b/pkg/grizzly/yaml.go @@ -47,7 +47,7 @@ func (parser *YAMLParser) Parse(file string, options ParserOptions) (Resources, } source := Source{ - Format: "yaml", + Format: formatYAML, Path: file, Rewritable: true, } diff --git a/pkg/mimir/client/http_client.go b/pkg/mimir/client/http_client.go index 71c7c3cb..673413ec 100644 --- a/pkg/mimir/client/http_client.go +++ b/pkg/mimir/client/http_client.go @@ -88,8 +88,8 @@ func (c *Client) doRequest(method string, url string, body []byte) ([]byte, erro } req.Header.Set("Content-Type", "application/yaml") - if c.config.ApiKey != "" { - req.SetBasicAuth(c.config.TenantID, c.config.ApiKey) + if c.config.APIKey != "" { + req.SetBasicAuth(c.config.TenantID, c.config.APIKey) } else { req.Header.Set("X-Scope-OrgID", fmt.Sprintf("%s", c.config.TenantID)) } diff --git a/pkg/mimir/rules-handler.go b/pkg/mimir/rules-handler.go index 0f2bf3e3..775293f1 100644 --- a/pkg/mimir/rules-handler.go +++ b/pkg/mimir/rules-handler.go @@ -2,10 +2,11 @@ package mimir import ( "fmt" + "strings" + "github.com/grafana/grizzly/pkg/mimir/client" "github.com/grafana/grizzly/pkg/mimir/models" - "strings" - + "github.com/grafana/grizzly/pkg/grizzly" ) @@ -54,8 +55,8 @@ func (h *RuleHandler) GetSpecUID(resource grizzly.Resource) (string, error) { } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *RuleHandler) GetByUID(UID string) (*grizzly.Resource, error) { - return h.getRemoteRuleGroup(UID) +func (h *RuleHandler) GetByUID(uid string) (*grizzly.Resource, error) { + return h.getRemoteRuleGroup(uid) } // GetRemote retrieves a datasource as a Resource diff --git a/pkg/syntheticmonitoring/httpclient.go b/pkg/syntheticmonitoring/httpclient.go index 4a887113..bb98cf69 100644 --- a/pkg/syntheticmonitoring/httpclient.go +++ b/pkg/syntheticmonitoring/httpclient.go @@ -7,7 +7,7 @@ import ( "time" ) -func NewHttpClient() (*http.Client, error) { +func NewHTTPClient() (*http.Client, error) { timeout := 10 * time.Second if timeoutStr := os.Getenv("GRIZZLY_HTTP_TIMEOUT"); timeoutStr != "" { timeoutSeconds, err := strconv.Atoi(timeoutStr) diff --git a/pkg/syntheticmonitoring/provider.go b/pkg/syntheticmonitoring/provider.go index d425cfff..85cee314 100644 --- a/pkg/syntheticmonitoring/provider.go +++ b/pkg/syntheticmonitoring/provider.go @@ -22,6 +22,9 @@ type ClientProvider interface { // NewProvider instantiates a new Provider. func NewProvider(config *config.SyntheticMonitoringConfig) (*Provider, error) { + if config.URL == "" { + config.URL = "https://synthetic-monitoring-api.grafana.net" + } if config.StackID == 0 { return nil, fmt.Errorf("stack id is not set") } @@ -34,6 +37,7 @@ func NewProvider(config *config.SyntheticMonitoringConfig) (*Provider, error) { if config.Token == "" { return nil, fmt.Errorf("token is not set") } + return &Provider{ config: config, }, nil @@ -67,12 +71,12 @@ func (p *Provider) GetHandlers() []grizzly.Handler { // NewClient creates a new client for synthetic monitoring go client func (p *Provider) Client() (*smapi.Client, error) { - client, err := NewHttpClient() + client, err := NewHTTPClient() if err != nil { return nil, err } - smClient := smapi.NewClient(smBaseURL, "", client) + smClient := smapi.NewClient(p.config.URL, "", client) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() diff --git a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go index d315463a..1996ca21 100644 --- a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go +++ b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go @@ -25,8 +25,6 @@ import ( * them to IDs, having requested an ID<->string mapping from the API. */ -const smBaseURL = "https://synthetic-monitoring-api.grafana.net" - type Probes struct { ByID map[int64]synthetic_monitoring.Probe ByName map[string]synthetic_monitoring.Probe @@ -35,7 +33,6 @@ type Probes struct { // SyntheticMonitoringHandler is a Grizzly Handler for Grafana Synthetic Monitoring type SyntheticMonitoringHandler struct { grizzly.BaseHandler - smProvider Provider } // NewSyntheticMonitoringHandler returns a Grizzly Handler for Grafana Synthetic Monitoring @@ -99,8 +96,8 @@ func (h *SyntheticMonitoringHandler) GetSpecUID(resource grizzly.Resource) (stri } // GetByUID retrieves JSON for a resource from an endpoint, by UID -func (h *SyntheticMonitoringHandler) GetByUID(UID string) (*grizzly.Resource, error) { - return h.getRemoteCheck(UID) +func (h *SyntheticMonitoringHandler) GetByUID(uid string) (*grizzly.Resource, error) { + return h.getRemoteCheck(uid) } // GetRemote retrieves a datasource as a Resource diff --git a/pkg/syntheticmonitoring/synthetic-monitoring_test.go b/pkg/syntheticmonitoring/synthetic-monitoring_test.go index 2b7bc1f1..d45473d1 100644 --- a/pkg/syntheticmonitoring/synthetic-monitoring_test.go +++ b/pkg/syntheticmonitoring/synthetic-monitoring_test.go @@ -11,7 +11,6 @@ import ( ) func TestSyntheticMonitoring(t *testing.T) { - t.Run("Check getUID is functioning correctly", func(t *testing.T) { resource := grizzly.Resource{ Body: map[string]any{