From 93b6a6bd8b937c2235ea55b708c11eda32d73743 Mon Sep 17 00:00:00 2001 From: malcolmholmes <42545407+malcolmholmes@users.noreply.github.com> Date: Fri, 19 Apr 2024 08:27:18 +0100 Subject: [PATCH 1/5] Add insecure-skip-verify for grafana (#363) * Add insecure-skip-verify for grafana * Add tls-host config value --- pkg/config/config.go | 2 ++ pkg/config/model.go | 8 +++++--- pkg/grafana/provider.go | 8 ++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 75a5a135..0df00f97 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -147,6 +147,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", diff --git a/pkg/config/model.go b/pkg/config/model.go index 9ba37a8e..676458cf 100644 --- a/pkg/config/model.go +++ b/pkg/config/model.go @@ -1,9 +1,11 @@ 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 { diff --git a/pkg/grafana/provider.go b/pkg/grafana/provider.go index f18dd0cf..29f4e72f 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" @@ -62,6 +63,13 @@ func (p *Provider) Client() (*gclient.GrafanaHTTPAPI, error) { 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 != "" { transportConfig.BasicAuth = url.UserPassword(p.config.User, p.config.Token) From 94e1ed905adde8ec58b68ce37a79dc2a95cfe5a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:33:26 +0100 Subject: [PATCH 2/5] Bump golang.org/x/net from 0.22.0 to 0.23.0 (#421) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0. - [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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= From cb554473f62593f41a64ca4c22b3df91b7bb7c0b Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Sat, 20 Apr 2024 09:20:01 -0400 Subject: [PATCH 3/5] Basic `golangci-lint` workflow (#422) --- Makefile | 7 +++++-- cmd/grr/workflow.go | 6 ++++-- pkg/config/config.go | 12 +++++++++--- pkg/config/model.go | 2 +- pkg/grafana/alertgroup-handler.go | 4 +++- pkg/grafana/dashboard-handler.go | 6 +++--- pkg/grafana/errors.go | 9 +++++++++ pkg/grafana/utils.go | 4 ++-- pkg/grizzly/server.go | 4 +++- pkg/grizzly/workflow.go | 2 +- pkg/mimir/cortex_tool.go | 2 +- pkg/mimir/rules-handler.go | 4 +++- .../synthetic-monitoring-handler.go | 1 - 13 files changed, 44 insertions(+), 19 deletions(-) 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/pkg/config/config.go b/pkg/config/config.go index 0df00f97..6e7e0ccb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -97,7 +97,9 @@ func NewConfig() { func GetContexts() error { contexts := map[string]interface{}{} currentContext := viper.GetString(CURRENT_CONTEXT) - viper.UnmarshalKey("contexts", &contexts) + if err := viper.UnmarshalKey("contexts", &contexts); err != nil { + return err + } keys := make([]string, 0, len(contexts)) for k := range contexts { keys = append(keys, k) @@ -115,7 +117,9 @@ 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) @@ -138,7 +142,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 } diff --git a/pkg/config/model.go b/pkg/config/model.go index 676458cf..e4d53316 100644 --- a/pkg/config/model.go +++ b/pkg/config/model.go @@ -10,7 +10,7 @@ type GrafanaConfig struct { type MimirConfig struct { Address string `yaml:"address" mapstructure:"address"` - TenantID int64 `yaml:"tenant-id" mapstructure:"tenant-id"` + TenantID string `yaml:"tenant-id" mapstructure:"tenant-id"` ApiKey string `yaml:"api-key" mapstructure:"api-key"` } diff --git a/pkg/grafana/alertgroup-handler.go b/pkg/grafana/alertgroup-handler.go index 33fa6efa..e80ac804 100644 --- a/pkg/grafana/alertgroup-handler.go +++ b/pkg/grafana/alertgroup-handler.go @@ -158,7 +158,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 { diff --git a/pkg/grafana/dashboard-handler.go b/pkg/grafana/dashboard-handler.go index 5db2959f..2629eed9 100644 --- a/pkg/grafana/dashboard-handler.go +++ b/pkg/grafana/dashboard-handler.go @@ -314,7 +314,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 +363,7 @@ func (h *DashboardHandler) DashboardJSONGetHandler(p grizzly.Server) http.Handle } out, _ := json.Marshal(wrapper) - w.Write(out) + writeOrLog(w, out) } } @@ -426,7 +426,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/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/utils.go b/pkg/grafana/utils.go index f5ff6f5f..853fc010 100644 --- a/pkg/grafana/utils.go +++ b/pkg/grafana/utils.go @@ -15,10 +15,10 @@ func extractFolderUID(client *gclient.GrafanaHTTPAPI, d models.DashboardFullWith 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 "" } diff --git a/pkg/grizzly/server.go b/pkg/grizzly/server.go index ee954bc2..7d2b1649 100644 --- a/pkg/grizzly/server.go +++ b/pkg/grizzly/server.go @@ -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..783b2847 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" ) diff --git a/pkg/mimir/cortex_tool.go b/pkg/mimir/cortex_tool.go index a816a67a..32c2af13 100644 --- a/pkg/mimir/cortex_tool.go +++ b/pkg/mimir/cortex_tool.go @@ -33,7 +33,7 @@ func (c *Cortex) ExecuteCortexTool(args ...string) ([]byte, error) { } cmd := exec.Command(path, args...) cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_ADDRESS=%s", c.config.Address)) - cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_TENANT_ID=%d", c.config.TenantID)) + cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_TENANT_ID=%s", c.config.TenantID)) cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_API_KEY=%s", c.config.ApiKey)) return exec.Command(path, args...).Output() } diff --git a/pkg/mimir/rules-handler.go b/pkg/mimir/rules-handler.go index 03997d04..f33feb7d 100644 --- a/pkg/mimir/rules-handler.go +++ b/pkg/mimir/rules-handler.go @@ -173,7 +173,9 @@ func (h *RuleHandler) writeRuleGroup(resource grizzly.Resource) error { if err != nil { return err } - os.WriteFile(tmpfile.Name(), out, 0644) + if err := os.WriteFile(tmpfile.Name(), out, 0644); err != nil { + return err + } output, err := h.cortexTool.ExecuteCortexTool("rules", "load", tmpfile.Name()) if err != nil { diff --git a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go index d315463a..552df53d 100644 --- a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go +++ b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go @@ -35,7 +35,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 From 4d0a79d660b308249b701f6fc3c60a5d13ca8513 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Sat, 20 Apr 2024 15:59:51 -0400 Subject: [PATCH 4/5] Enable more linters (#423) Follow-up to https://github.com/grafana/grizzly/pull/422 Enable the same linters that we use in the TF provider. All of the fixes are super simple. Changing casing and creating consts mostly. --- .golangci.toml | 24 +++++++++++++++ integration/folder_test.go | 2 +- integration/pull_test.go | 1 - pkg/config/config.go | 21 +++++++------ pkg/config/model.go | 2 +- pkg/grafana/alertgroup-handler.go | 9 ++---- pkg/grafana/contactpoint-handler.go | 6 ++-- pkg/grafana/dashboard-handler.go | 25 ++++++++-------- pkg/grafana/datasource-handler.go | 4 +-- pkg/grafana/folder-handler.go | 23 +++++++------- pkg/grafana/library-element-handler.go | 6 ++-- pkg/grafana/notificationpolicy-handler.go | 3 +- pkg/grafana/provider.go | 10 +++---- pkg/grafana/utils.go | 17 ++++++----- pkg/grafana/utils_test.go | 2 +- pkg/grizzly/formatting.go | 13 ++++++-- pkg/grizzly/handler.go | 2 +- pkg/grizzly/json.go | 2 +- pkg/grizzly/parsing.go | 6 ++-- pkg/grizzly/parsing_test.go | 2 +- pkg/grizzly/registry.go | 8 ++--- pkg/grizzly/resources.go | 1 - pkg/grizzly/server.go | 4 +-- pkg/grizzly/workflow.go | 30 +++++++++---------- pkg/grizzly/yaml.go | 2 +- pkg/mimir/cortex_tool.go | 2 +- pkg/mimir/provider.go | 2 +- pkg/mimir/rules-handler.go | 4 +-- pkg/syntheticmonitoring/httpclient.go | 2 +- pkg/syntheticmonitoring/provider.go | 2 +- .../synthetic-monitoring-handler.go | 4 +-- .../synthetic-monitoring_test.go | 1 - 32 files changed, 132 insertions(+), 110 deletions(-) create mode 100644 .golangci.toml 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/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 6e7e0ccb..bbb296c6 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() { @@ -70,7 +69,7 @@ func Mock(values map[string]interface{}) { } func Import() error { - name := viper.GetString(CURRENT_CONTEXT) + name := viper.GetString(CurrentContextSetting) if name == "" { NewConfig() return Import() @@ -90,13 +89,13 @@ 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) + currentContext := viper.GetString(CurrentContextSetting) if err := viper.UnmarshalKey("contexts", &contexts); err != nil { return err } @@ -122,7 +121,7 @@ func UseContext(context string) error { } for k := range contexts { if k == context { - viper.Set(CURRENT_CONTEXT, context) + viper.Set(CurrentContextSetting, context) return Write() } } @@ -130,7 +129,7 @@ func UseContext(context string) error { } func CurrentContext() (*Context, error) { - name := viper.GetString(CURRENT_CONTEXT) + name := viper.GetString(CurrentContextSetting) if name == "" { NewConfig() return CurrentContext() @@ -168,7 +167,7 @@ var acceptableKeys = map[string]string{ } 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) @@ -191,7 +190,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 { @@ -230,7 +229,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) { @@ -261,7 +260,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 e4d53316..0d71b904 100644 --- a/pkg/config/model.go +++ b/pkg/config/model.go @@ -11,7 +11,7 @@ type GrafanaConfig struct { 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 { diff --git a/pkg/grafana/alertgroup-handler.go b/pkg/grafana/alertgroup-handler.go index e80ac804..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 @@ -173,7 +172,6 @@ func (h *AlertRuleGroupHandler) createAlertRuleGroup(resource grizzly.Resource) return err } - stringtrue := "true" params := provisioning.NewPutAlertRuleGroupParams(). WithBody(&group). WithGroup(group.Title). @@ -201,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) @@ -209,7 +206,6 @@ func (h *AlertRuleGroupHandler) updateAlertRule(rule *models.ProvisionedAlertRul return err } - stringtrue := "true" params := provisioning.NewPutAlertRuleParams(). WithUID(rule.UID). WithBody(rule). @@ -274,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 2629eed9..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), }, } 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/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 29f4e72f..8cb90b69 100644 --- a/pkg/grafana/provider.go +++ b/pkg/grafana/provider.go @@ -53,17 +53,17 @@ 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 { + if parsedURL.Scheme == "https" && p.config.InsecureSkipVerify { transportConfig.TLSConfig = &tls.Config{ InsecureSkipVerify: true, ServerName: p.config.TLSHost, diff --git a/pkg/grafana/utils.go b/pkg/grafana/utils.go index 853fc010..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 { // nolint:staticcheck + if d.Meta.FolderID == generalFolderID { // nolint:staticcheck return generalFolderUID } - folder, err := getFolderById(client, d.Meta.FolderID) // nolint:staticcheck + 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 7d2b1649..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()) } diff --git a/pkg/grizzly/workflow.go b/pkg/grizzly/workflow.go index 783b2847..ccf12441 100644 --- a/pkg/grizzly/workflow.go +++ b/pkg/grizzly/workflow.go @@ -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/cortex_tool.go b/pkg/mimir/cortex_tool.go index 32c2af13..9ddae32c 100644 --- a/pkg/mimir/cortex_tool.go +++ b/pkg/mimir/cortex_tool.go @@ -34,6 +34,6 @@ func (c *Cortex) ExecuteCortexTool(args ...string) ([]byte, error) { cmd := exec.Command(path, args...) cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_ADDRESS=%s", c.config.Address)) cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_TENANT_ID=%s", c.config.TenantID)) - cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_API_KEY=%s", c.config.ApiKey)) + cmd.Env = append(cmd.Env, fmt.Sprintf("CORTEX_API_KEY=%s", c.config.APIKey)) return exec.Command(path, args...).Output() } diff --git a/pkg/mimir/provider.go b/pkg/mimir/provider.go index 563ac615..1b7234e3 100644 --- a/pkg/mimir/provider.go +++ b/pkg/mimir/provider.go @@ -22,7 +22,7 @@ func NewProvider(config *config.MimirConfig) (*Provider, error) { if config.Address == "" { return nil, fmt.Errorf("mimir address is not set") } - if config.ApiKey == "" { + if config.APIKey == "" { return nil, fmt.Errorf("mimir api key is not set") } diff --git a/pkg/mimir/rules-handler.go b/pkg/mimir/rules-handler.go index f33feb7d..6abf25b7 100644 --- a/pkg/mimir/rules-handler.go +++ b/pkg/mimir/rules-handler.go @@ -55,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..de4e3cb2 100644 --- a/pkg/syntheticmonitoring/provider.go +++ b/pkg/syntheticmonitoring/provider.go @@ -67,7 +67,7 @@ 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 } diff --git a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go index 552df53d..904a99a6 100644 --- a/pkg/syntheticmonitoring/synthetic-monitoring-handler.go +++ b/pkg/syntheticmonitoring/synthetic-monitoring-handler.go @@ -98,8 +98,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{ From f7c171e48e49970e97686a84c2d505c742b5bc9a Mon Sep 17 00:00:00 2001 From: Selene Date: Mon, 22 Apr 2024 11:18:03 +0200 Subject: [PATCH 5/5] Add region to SM backend URL (#420) * Add region to SM backend URL * Remove id from region * Add exception list for SM urls and fix the region in the url * Set URL instead of region * Remove unused map * Set default URL when its empty --- .gitignore | 2 ++ docs/content/configuration.md | 36 ++++++++++--------- pkg/config/config.go | 2 ++ pkg/config/model.go | 1 + pkg/syntheticmonitoring/provider.go | 6 +++- .../synthetic-monitoring-handler.go | 2 -- 6 files changed, 30 insertions(+), 19 deletions(-) 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/docs/content/configuration.md b/docs/content/configuration.md index 5e31b6c6..8e1fe9ea 100644 --- a/docs/content/configuration.md +++ b/docs/content/configuration.md @@ -55,8 +55,10 @@ grr config set synthetic-monitoring.token abcdef123456 # API key (must have Metr grr config set synthetic-monitoring.stack-id # Grafana stack ID grr config set synthetic-monitoring.metrics-id # Metrics instance ID grr config set synthetic-monitoring.logs-id # Logs instance ID +grr config set synthetic-monitoring.url # 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 @@ -135,11 +137,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. @@ -147,25 +149,27 @@ 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: -| 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 | +| 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 | Note, this will also work with other Cortex 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/pkg/config/config.go b/pkg/config/config.go index bbb296c6..30cf9c84 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -37,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": "CORTEX_ADDRESS", "mimir.tenant-id": "CORTEX_TENANT_ID", @@ -161,6 +162,7 @@ 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", diff --git a/pkg/config/model.go b/pkg/config/model.go index 0d71b904..7dc2bcfa 100644 --- a/pkg/config/model.go +++ b/pkg/config/model.go @@ -19,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/syntheticmonitoring/provider.go b/pkg/syntheticmonitoring/provider.go index de4e3cb2..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 @@ -72,7 +76,7 @@ func (p *Provider) Client() (*smapi.Client, error) { 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 904a99a6..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