Skip to content

Commit

Permalink
Merge remote-tracking branch 'grafana/master' into backend-service-in…
Browse files Browse the repository at this point in the history
…-grafanaui

* grafana/master:
  GraphPanel: Don't sort series when legend table & sort column is not visible  (grafana#17095)
  Chore: Update grafana-ui version to 6.2.0-alpha.0 (grafana#17109)
  add support for periodically reloading mysql client certs (grafana#14892)
  Chore: Deduplicate sqlstore transaction code (grafana#17069)
  Alertmanager: Replace illegal chars with underscore in label names (grafana#17002)
  Adjusted documentation for gcs to reflect the code (grafana#16947)
  fix: Initial url update in Explore should replace existing url history grafana#17030 (grafana#17061)
  Explore: Allow switching between metrics and logs  (grafana#16959)
  Chore: explore possibilities of using makefile (grafana#17039)
  • Loading branch information
ryantxu committed May 17, 2019
2 parents a208cf9 + 0a9863a commit 2093d7e
Show file tree
Hide file tree
Showing 36 changed files with 439 additions and 209 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ debug.test
/scripts/build/release_publisher/release_publisher
*.patch


# Ignoring frontend packages specifics
/packages/**/dist
/packages/**/compiled
/packages/**/.rpt2_cache

theOutput/
theOutput/

# Ignore go local build dependencies
/scripts/go/bin/**
26 changes: 25 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
-include local/Makefile

.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean
.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean gosec revive

GO := GO111MODULE=on go
GO_FILES := ./pkg/...

all: deps build

Expand Down Expand Up @@ -66,3 +69,24 @@ clean:
node_modules: package.json yarn.lock
@echo "install frontend dependencies"
yarn install --pure-lockfile --no-progress

scripts/go/bin/revive: scripts/go/go.mod
@cd scripts/go; \
$(GO) build -o ./bin/revive github.com/mgechev/revive

scripts/go/bin/gosec: scripts/go/go.mod
@cd scripts/go; \
$(GO) build -o ./bin/gosec github.com/securego/gosec/cmd/gosec

revive: scripts/go/bin/revive
@scripts/go/bin/revive \
-formatter stylish \
-config ./scripts/go/configs/revive.toml \
$(GO_FILES)

# TODO recheck the rules and leave only necessary exclusions
gosec: scripts/go/bin/gosec
@scripts/go/bin/gosec -quiet \
-exclude=G104,G107,G201,G202,G204,G301,G304,G401,G402,G501 \
-conf=./scripts/go/configs/gosec.json \
$(GO_FILES)
2 changes: 1 addition & 1 deletion docs/sources/installation/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ Service Account keys can be created and downloaded from https://console.develope

Service Account should have "Storage Object Writer" role. The access control model of the bucket needs to be "Set object-level and bucket-level permissions". Grafana itself will make the images public readable.

### bucket name
### bucket
Bucket Name on Google Cloud Storage.

### path
Expand Down
2 changes: 1 addition & 1 deletion packages/grafana-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@grafana/ui",
"version": "6.0.1-alpha.0",
"version": "6.2.0-alpha.0",
"description": "Grafana Components Library",
"keywords": [
"typescript",
Expand Down
12 changes: 6 additions & 6 deletions pkg/cmd/grafana-cli/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import (
)

var (
IoHelper m.IoUtil = IoUtilImp{}
HttpClient http.Client
grafanaVersion string
NotFoundError = errors.New("404 not found error")
IoHelper m.IoUtil = IoUtilImp{}
HttpClient http.Client
grafanaVersion string
ErrNotFoundError = errors.New("404 not found error")
)

func Init(version string, skipTLSVerify bool) {
Expand Down Expand Up @@ -131,7 +131,7 @@ func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) {

if err != nil {
logger.Info("Failed to send request: ", err)
if err == NotFoundError {
if err == ErrNotFoundError {
return m.Plugin{}, fmt.Errorf("Failed to find requested plugin, check if the plugin_id is correct. error: %v", err)
}
return m.Plugin{}, fmt.Errorf("Failed to send request. error: %v", err)
Expand Down Expand Up @@ -174,7 +174,7 @@ func sendRequest(repoUrl string, subPaths ...string) ([]byte, error) {
}

if res.StatusCode == 404 {
return []byte{}, NotFoundError
return []byte{}, ErrNotFoundError
}
if res.StatusCode/100 != 2 {
return []byte{}, fmt.Errorf("Api returned invalid status: %s", res.Status)
Expand Down
10 changes: 5 additions & 5 deletions pkg/cmd/grafana-cli/utils/grafana_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ func isDevEnvironment() bool {
defaultsPath := filepath.Join(exPath, "../conf/defaults.ini")
_, err = os.Stat(defaultsPath)
return err == nil
} else {
// But at the same time there are per platform directories that contain the binaries and can also be used.
defaultsPath := filepath.Join(exPath, "../../conf/defaults.ini")
_, err = os.Stat(defaultsPath)
return err == nil
}

// But at the same time there are per platform directories that contain the binaries and can also be used.
defaultsPath := filepath.Join(exPath, "../../conf/defaults.ini")
_, err = os.Stat(defaultsPath)
return err == nil
}

func returnOsDefault(currentOs string) string {
Expand Down
45 changes: 30 additions & 15 deletions pkg/services/alerting/notifiers/alertmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package notifiers

import (
"context"
"regexp"
"time"

"github.com/grafana/grafana/pkg/bus"
Expand All @@ -27,6 +28,7 @@ func init() {
})
}

// NewAlertmanagerNotifier returns a new Alertmanager notifier
func NewAlertmanagerNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
Expand All @@ -35,38 +37,42 @@ func NewAlertmanagerNotifier(model *models.AlertNotification) (alerting.Notifier

return &AlertmanagerNotifier{
NotifierBase: NewNotifierBase(model),
Url: url,
URL: url,
log: log.New("alerting.notifier.prometheus-alertmanager"),
}, nil
}

// AlertmanagerNotifier sends alert notifications to the alert manager
type AlertmanagerNotifier struct {
NotifierBase
Url string
URL string
log log.Logger
}

func (this *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *models.AlertNotificationState) bool {
this.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)
// ShouldNotify returns true if the notifiers should be used depending on state
func (am *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *models.AlertNotificationState) bool {
am.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)

// Do not notify when we become OK for the first time.
if (evalContext.PrevAlertState == models.AlertStatePending) && (evalContext.Rule.State == models.AlertStateOK) {
return false
}

// Notify on Alerting -> OK to resolve before alertmanager timeout.
if (evalContext.PrevAlertState == models.AlertStateAlerting) && (evalContext.Rule.State == models.AlertStateOK) {
return true
}

return evalContext.Rule.State == models.AlertStateAlerting
}

func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleUrl string) *simplejson.Json {
func (am *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleURL string) *simplejson.Json {
alertJSON := simplejson.New()
alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339))
if evalContext.Rule.State == models.AlertStateOK {
alertJSON.Set("endsAt", time.Now().UTC().Format(time.RFC3339))
}
alertJSON.Set("generatorURL", ruleUrl)
alertJSON.Set("generatorURL", ruleURL)

// Annotations (summary and description are very commonly used).
alertJSON.SetPath([]string{"annotations", "summary"}, evalContext.Rule.Name)
Expand Down Expand Up @@ -94,7 +100,7 @@ func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext,
tags["metric"] = match.Metric
} else {
for k, v := range match.Tags {
tags[k] = v
tags[replaceIllegalCharsInLabelname(k)] = v
}
}
}
Expand All @@ -103,41 +109,50 @@ func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext,
return alertJSON
}

func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
this.log.Info("Sending Alertmanager alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)
// Notify sends alert notifications to the alert manager
func (am *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
am.log.Info("Sending Alertmanager alert", "ruleId", evalContext.Rule.Id, "notification", am.Name)

ruleUrl, err := evalContext.GetRuleUrl()
ruleURL, err := evalContext.GetRuleUrl()
if err != nil {
this.log.Error("Failed get rule link", "error", err)
am.log.Error("Failed get rule link", "error", err)
return err
}

// Send one alert per matching series.
alerts := make([]interface{}, 0)
for _, match := range evalContext.EvalMatches {
alert := this.createAlert(evalContext, match, ruleUrl)
alert := am.createAlert(evalContext, match, ruleURL)
alerts = append(alerts, alert)
}

// This happens on ExecutionError or NoData
if len(alerts) == 0 {
alert := this.createAlert(evalContext, nil, ruleUrl)
alert := am.createAlert(evalContext, nil, ruleURL)
alerts = append(alerts, alert)
}

bodyJSON := simplejson.NewFromAny(alerts)
body, _ := bodyJSON.MarshalJSON()

cmd := &models.SendWebhookSync{
Url: this.Url + "/api/v1/alerts",
Url: am.URL + "/api/v1/alerts",
HttpMethod: "POST",
Body: string(body),
}

if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
this.log.Error("Failed to send alertmanager", "error", err, "alertmanager", this.Name)
am.log.Error("Failed to send alertmanager", "error", err, "alertmanager", am.Name)
return err
}

return nil
}

// regexp that matches all none valid label name characters
// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
var labelNamePattern = regexp.MustCompile(`[^a-zA-Z0-9_]`)

func replaceIllegalCharsInLabelname(input string) string {
return labelNamePattern.ReplaceAllString(input, "_")
}
27 changes: 24 additions & 3 deletions pkg/services/alerting/notifiers/alertmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,35 @@ import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"

"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)

func TestReplaceIllegalCharswithUnderscore(t *testing.T) {
cases := []struct {
input string
expected string
}{
{
input: "foobar",
expected: "foobar",
},
{
input: `foo.,\][!?#="~*^&+|<>\'bar09_09`,
expected: "foo____________________bar09_09",
},
}

for _, c := range cases {
assert.Equal(t, replaceIllegalCharsInLabelname(c.input), c.expected)
}
}

func TestWhenAlertManagerShouldNotify(t *testing.T) {
tcs := []struct {
prevState models.AlertStateType
Expand Down Expand Up @@ -43,7 +64,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {

for _, tc := range tcs {
am := &AlertmanagerNotifier{log: log.New("test.logger")}
evalContext := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState,
})

Expand Down Expand Up @@ -88,7 +109,7 @@ func TestAlertmanagerNotifier(t *testing.T) {
alertmanagerNotifier := not.(*AlertmanagerNotifier)

So(err, ShouldBeNil)
So(alertmanagerNotifier.Url, ShouldEqual, "http://127.0.0.1:9093/")
So(alertmanagerNotifier.URL, ShouldEqual, "http://127.0.0.1:9093/")
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/login/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var (
ErrProviderDeniedRequest = errors.New("Login provider denied login request")
ErrSignUpNotAllowed = errors.New("Signup is not allowed for this adapter")
ErrTooManyLoginAttempts = errors.New("Too many consecutive incorrect login attempts for user. Login for user temporarily blocked")
ErrPasswordEmpty = errors.New("No password provided.")
ErrPasswordEmpty = errors.New("No password provided")
ErrUsersQuotaReached = errors.New("Users quota reached")
ErrGettingUserQuota = errors.New("Error getting user quota")
)
6 changes: 2 additions & 4 deletions pkg/services/provisioning/dashboards/dashboard_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ func (dpm *DashboardProvisionerMock) Provision() error {
dpm.Calls.Provision = append(dpm.Calls.Provision, nil)
if dpm.ProvisionFunc != nil {
return dpm.ProvisionFunc()
} else {
return nil
}
return nil
}

func (dpm *DashboardProvisionerMock) PollChanges(ctx context.Context) {
Expand All @@ -41,7 +40,6 @@ func (dpm *DashboardProvisionerMock) GetProvisionerResolvedPath(name string) str
dpm.Calls.PollChanges = append(dpm.Calls.GetProvisionerResolvedPath, name)
if dpm.GetProvisionerResolvedPathFunc != nil {
return dpm.GetProvisionerResolvedPathFunc(name)
} else {
return ""
}
return ""
}
12 changes: 4 additions & 8 deletions pkg/services/provisioning/provisioning_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,30 @@ func (mock *ProvisioningServiceMock) ProvisionDatasources() error {
mock.Calls.ProvisionDatasources = append(mock.Calls.ProvisionDatasources, nil)
if mock.ProvisionDatasourcesFunc != nil {
return mock.ProvisionDatasourcesFunc()
} else {
return nil
}
return nil
}

func (mock *ProvisioningServiceMock) ProvisionNotifications() error {
mock.Calls.ProvisionNotifications = append(mock.Calls.ProvisionNotifications, nil)
if mock.ProvisionNotificationsFunc != nil {
return mock.ProvisionNotificationsFunc()
} else {
return nil
}
return nil
}

func (mock *ProvisioningServiceMock) ProvisionDashboards() error {
mock.Calls.ProvisionDashboards = append(mock.Calls.ProvisionDashboards, nil)
if mock.ProvisionDashboardsFunc != nil {
return mock.ProvisionDashboardsFunc()
} else {
return nil
}
return nil
}

func (mock *ProvisioningServiceMock) GetDashboardProvisionerResolvedPath(name string) string {
mock.Calls.GetDashboardProvisionerResolvedPath = append(mock.Calls.GetDashboardProvisionerResolvedPath, name)
if mock.GetDashboardProvisionerResolvedPathFunc != nil {
return mock.GetDashboardProvisionerResolvedPathFunc(name)
} else {
return ""
}
return ""
}
2 changes: 1 addition & 1 deletion pkg/services/provisioning/values/values.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// A set of value types to use in provisioning. They add custom unmarshaling logic that puts the string values
// Package values is a set of value types to use in provisioning. They add custom unmarshaling logic that puts the string values
// through os.ExpandEnv.
// Usage:
// type Data struct {
Expand Down
Loading

0 comments on commit 2093d7e

Please sign in to comment.