Merge remote-tracking branch 'origin/master' into stackdriver-plugin
This commit is contained in:
@@ -29,7 +29,7 @@ func TestFormatShort(t *testing.T) {
|
||||
}
|
||||
|
||||
if parsed != tc.interval {
|
||||
t.Errorf("expectes the parsed duration to equal the interval. Got %v expected: %v", parsed, tc.interval)
|
||||
t.Errorf("expects the parsed duration to equal the interval. Got %v expected: %v", parsed, tc.interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-9
@@ -41,15 +41,16 @@ func (hs *HTTPServer) RenderToPng(c *m.ReqContext) {
|
||||
}
|
||||
|
||||
result, err := hs.RenderService.Render(c.Req.Context(), rendering.Opts{
|
||||
Width: width,
|
||||
Height: height,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
Path: c.Params("*") + queryParams,
|
||||
Timezone: queryReader.Get("tz", ""),
|
||||
Encoding: queryReader.Get("encoding", ""),
|
||||
Width: width,
|
||||
Height: height,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
Path: c.Params("*") + queryParams,
|
||||
Timezone: queryReader.Get("tz", ""),
|
||||
Encoding: queryReader.Get("encoding", ""),
|
||||
ConcurrentLimit: 30,
|
||||
})
|
||||
|
||||
if err != nil && err == rendering.ErrTimeout {
|
||||
|
||||
@@ -112,7 +112,7 @@ func SelectVersion(plugin m.Plugin, version string) (m.Version, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return m.Version{}, errors.New("Could not find the version your looking for")
|
||||
return m.Version{}, errors.New("Could not find the version you're looking for")
|
||||
}
|
||||
|
||||
func RemoveGitBuildFromName(pluginName, filename string) string {
|
||||
|
||||
@@ -52,7 +52,7 @@ func (az *AzureBlobUploader) Upload(ctx context.Context, imageDiskPath string) (
|
||||
}
|
||||
randomFileName := util.GetRandomString(30) + ".png"
|
||||
// upload image
|
||||
az.log.Debug("Uploading image to azure_blob", "conatiner_name", az.container_name, "blob_name", randomFileName)
|
||||
az.log.Debug("Uploading image to azure_blob", "container_name", az.container_name, "blob_name", randomFileName)
|
||||
resp, err := blob.FileUpload(az.container_name, randomFileName, file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -274,10 +274,10 @@ func (a *Auth) canonicalizedHeaders(req *http.Request) string {
|
||||
}
|
||||
}
|
||||
|
||||
splitted := strings.Split(buffer.String(), "\n")
|
||||
sort.Strings(splitted)
|
||||
split := strings.Split(buffer.String(), "\n")
|
||||
sort.Strings(split)
|
||||
|
||||
return strings.Join(splitted, "\n")
|
||||
return strings.Join(split, "\n")
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -313,8 +313,8 @@ func (a *Auth) canonicalizedResource(req *http.Request) string {
|
||||
buffer.WriteString(fmt.Sprintf("\n%s:%s", key, strings.Join(values, ",")))
|
||||
}
|
||||
|
||||
splitted := strings.Split(buffer.String(), "\n")
|
||||
sort.Strings(splitted)
|
||||
split := strings.Split(buffer.String(), "\n")
|
||||
sort.Strings(split)
|
||||
|
||||
return strings.Join(splitted, "\n")
|
||||
return strings.Join(split, "\n")
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ func (j *Json) StringArray() ([]string, error) {
|
||||
|
||||
// MustArray guarantees the return of a `[]interface{}` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over array values in a succinct manner:
|
||||
// useful when you want to iterate over array values in a succinct manner:
|
||||
// for i, v := range js.Get("results").MustArray() {
|
||||
// fmt.Println(i, v)
|
||||
// }
|
||||
@@ -281,7 +281,7 @@ func (j *Json) MustArray(args ...[]interface{}) []interface{} {
|
||||
|
||||
// MustMap guarantees the return of a `map[string]interface{}` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over map values in a succinct manner:
|
||||
// useful when you want to iterate over map values in a succinct manner:
|
||||
// for k, v := range js.Get("dictionary").MustMap() {
|
||||
// fmt.Println(k, v)
|
||||
// }
|
||||
@@ -329,7 +329,7 @@ func (j *Json) MustString(args ...string) string {
|
||||
|
||||
// MustStringArray guarantees the return of a `[]string` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over array values in a succinct manner:
|
||||
// useful when you want to iterate over array values in a succinct manner:
|
||||
// for i, s := range js.Get("results").MustStringArray() {
|
||||
// fmt.Println(i, s)
|
||||
// }
|
||||
|
||||
@@ -48,7 +48,7 @@ type LdapAttributeMap struct {
|
||||
type LdapGroupToOrgRole struct {
|
||||
GroupDN string `toml:"group_dn"`
|
||||
OrgId int64 `toml:"org_id"`
|
||||
IsGrafanaAdmin *bool `toml:"grafana_admin"` // This is a pointer to know if it was set or not (for backwards compatability)
|
||||
IsGrafanaAdmin *bool `toml:"grafana_admin"` // This is a pointer to know if it was set or not (for backwards compatibility)
|
||||
OrgRole m.RoleType `toml:"org_role"`
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ type GetLatestNotificationQuery struct {
|
||||
AlertId int64
|
||||
NotifierId int64
|
||||
|
||||
Result *AlertNotificationJournal
|
||||
Result []AlertNotificationJournal
|
||||
}
|
||||
|
||||
type CleanNotificationJournalCommand struct {
|
||||
|
||||
@@ -82,12 +82,13 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
|
||||
if collapsed && collapsedJSON.MustBool() {
|
||||
|
||||
// extract alerts from sub panels for collapsed panels
|
||||
als, err := e.getAlertFromPanels(panel, validateAlertFunc)
|
||||
alertSlice, err := e.getAlertFromPanels(panel,
|
||||
validateAlertFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
alerts = append(alerts, als...)
|
||||
alerts = append(alerts, alertSlice...)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
@@ -67,7 +68,7 @@ func (n *notificationService) sendNotifications(evalContext *EvalContext, notifi
|
||||
|
||||
// Verify that we can send the notification again
|
||||
// but this time within the same transaction.
|
||||
if !evalContext.IsTestRun && !not.ShouldNotify(context.Background(), evalContext) {
|
||||
if !evalContext.IsTestRun && !not.ShouldNotify(ctx, evalContext) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -108,11 +109,12 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
|
||||
}
|
||||
|
||||
renderOpts := rendering.Opts{
|
||||
Width: 1000,
|
||||
Height: 500,
|
||||
Timeout: alertTimeout / 2,
|
||||
OrgId: context.Rule.OrgId,
|
||||
OrgRole: m.ROLE_ADMIN,
|
||||
Width: 1000,
|
||||
Height: 500,
|
||||
Timeout: alertTimeout / 2,
|
||||
OrgId: context.Rule.OrgId,
|
||||
OrgRole: m.ROLE_ADMIN,
|
||||
ConcurrentLimit: setting.AlertingRenderLimit,
|
||||
}
|
||||
|
||||
ref, err := context.GetDashboardUID()
|
||||
|
||||
@@ -42,12 +42,21 @@ func NewNotifierBase(model *models.AlertNotification) NotifierBase {
|
||||
}
|
||||
}
|
||||
|
||||
func defaultShouldNotify(context *alerting.EvalContext, sendReminder bool, frequency time.Duration, lastNotify time.Time) bool {
|
||||
func defaultShouldNotify(context *alerting.EvalContext, sendReminder bool, frequency time.Duration, journals []models.AlertNotificationJournal) bool {
|
||||
// Only notify on state change.
|
||||
if context.PrevAlertState == context.Rule.State && !sendReminder {
|
||||
return false
|
||||
}
|
||||
|
||||
// get last successfully sent notification
|
||||
lastNotify := time.Time{}
|
||||
for _, j := range journals {
|
||||
if j.Success {
|
||||
lastNotify = time.Unix(j.SentAt, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Do not notify if interval has not elapsed
|
||||
if sendReminder && !lastNotify.IsZero() && lastNotify.Add(frequency).After(time.Now()) {
|
||||
return false
|
||||
@@ -75,20 +84,12 @@ func (n *NotifierBase) ShouldNotify(ctx context.Context, c *alerting.EvalContext
|
||||
}
|
||||
|
||||
err := bus.DispatchCtx(ctx, cmd)
|
||||
if err == models.ErrJournalingNotFound {
|
||||
return true
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n.log.Error("Could not determine last time alert notifier fired", "Alert name", c.Rule.Name, "Error", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !cmd.Result.Success {
|
||||
return true
|
||||
}
|
||||
|
||||
return defaultShouldNotify(c, n.SendReminder, n.Frequency, time.Unix(cmd.Result.SentAt, 0))
|
||||
return defaultShouldNotify(c, n.SendReminder, n.Frequency, cmd.Result)
|
||||
}
|
||||
|
||||
func (n *NotifierBase) GetType() string {
|
||||
|
||||
@@ -15,51 +15,105 @@ import (
|
||||
)
|
||||
|
||||
func TestShouldSendAlertNotification(t *testing.T) {
|
||||
tnow := time.Now()
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
prevState m.AlertStateType
|
||||
newState m.AlertStateType
|
||||
expected bool
|
||||
sendReminder bool
|
||||
frequency time.Duration
|
||||
journals []m.AlertNotificationJournal
|
||||
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
name: "pending -> ok should not trigger an notification",
|
||||
newState: m.AlertStatePending,
|
||||
prevState: m.AlertStateOK,
|
||||
expected: false,
|
||||
name: "pending -> ok should not trigger an notification",
|
||||
newState: m.AlertStatePending,
|
||||
prevState: m.AlertStateOK,
|
||||
sendReminder: false,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "ok -> alerting should trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStateAlerting,
|
||||
expected: true,
|
||||
name: "ok -> alerting should trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStateAlerting,
|
||||
sendReminder: false,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "ok -> pending should not trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStatePending,
|
||||
expected: false,
|
||||
name: "ok -> pending should not trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStatePending,
|
||||
sendReminder: false,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "ok -> ok should not trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStateOK,
|
||||
expected: false,
|
||||
sendReminder: false,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "ok -> alerting should not trigger an notification",
|
||||
name: "ok -> alerting should trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStateAlerting,
|
||||
expected: true,
|
||||
sendReminder: true,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "ok -> ok with reminder should not trigger an notification",
|
||||
newState: m.AlertStateOK,
|
||||
prevState: m.AlertStateOK,
|
||||
expected: false,
|
||||
sendReminder: true,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "alerting -> alerting with reminder and no journaling should trigger",
|
||||
newState: m.AlertStateAlerting,
|
||||
prevState: m.AlertStateAlerting,
|
||||
frequency: time.Minute * 10,
|
||||
sendReminder: true,
|
||||
journals: []m.AlertNotificationJournal{},
|
||||
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "alerting -> alerting with reminder and successful recent journal event should not trigger",
|
||||
newState: m.AlertStateAlerting,
|
||||
prevState: m.AlertStateAlerting,
|
||||
frequency: time.Minute * 10,
|
||||
sendReminder: true,
|
||||
journals: []m.AlertNotificationJournal{
|
||||
{SentAt: tnow.Add(-time.Minute).Unix(), Success: true},
|
||||
},
|
||||
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "alerting -> alerting with reminder and failed recent journal event should trigger",
|
||||
newState: m.AlertStateAlerting,
|
||||
prevState: m.AlertStateAlerting,
|
||||
frequency: time.Minute * 10,
|
||||
sendReminder: true,
|
||||
expect: true,
|
||||
journals: []m.AlertNotificationJournal{
|
||||
{SentAt: tnow.Add(-time.Minute).Unix(), Success: false}, // recent failed notification
|
||||
{SentAt: tnow.Add(-time.Hour).Unix(), Success: true}, // old successful notification
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -69,8 +123,8 @@ func TestShouldSendAlertNotification(t *testing.T) {
|
||||
})
|
||||
|
||||
evalContext.Rule.State = tc.prevState
|
||||
if defaultShouldNotify(evalContext, true, 0, time.Now()) != tc.expected {
|
||||
t.Errorf("failed %s. expected %+v to return %v", tc.name, tc, tc.expected)
|
||||
if defaultShouldNotify(evalContext, true, tc.frequency, tc.journals) != tc.expect {
|
||||
t.Errorf("failed test %s.\n expected \n%+v \nto return: %v", tc.name, tc, tc.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,16 +141,6 @@ func TestShouldNotifyWhenNoJournalingIsFound(t *testing.T) {
|
||||
})
|
||||
evalContext := alerting.NewEvalContext(context.TODO(), &alerting.Rule{})
|
||||
|
||||
Convey("should notify if no journaling is found", func() {
|
||||
bus.AddHandlerCtx("", func(ctx context.Context, q *m.GetLatestNotificationQuery) error {
|
||||
return m.ErrJournalingNotFound
|
||||
})
|
||||
|
||||
if !notifier.ShouldNotify(context.Background(), evalContext) {
|
||||
t.Errorf("should send notifications when ErrJournalingNotFound is returned")
|
||||
}
|
||||
})
|
||||
|
||||
Convey("should not notify query returns error", func() {
|
||||
bus.AddHandlerCtx("", func(ctx context.Context, q *m.GetLatestNotificationQuery) error {
|
||||
return errors.New("some kind of error unknown error")
|
||||
|
||||
@@ -74,7 +74,7 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
message := ""
|
||||
if evalContext.Rule.State != m.AlertStateOK { //dont add message when going back to alert state ok.
|
||||
if evalContext.Rule.State != m.AlertStateOK { //don't add message when going back to alert state ok.
|
||||
message = evalContext.Rule.Message
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
handler.notifier.SendIfNeeded(evalContext)
|
||||
|
||||
handler.notifier.SendIfNeeded(evalContext)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
|
||||
}
|
||||
|
||||
if dashboards[i].UpdateIntervalSeconds == 0 {
|
||||
dashboards[i].UpdateIntervalSeconds = 3
|
||||
dashboards[i].UpdateIntervalSeconds = 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func validateDashboardAsConfig(t *testing.T, cfg []*DashboardsAsConfig) {
|
||||
So(len(ds.Options), ShouldEqual, 1)
|
||||
So(ds.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
||||
So(ds.DisableDeletion, ShouldBeTrue)
|
||||
So(ds.UpdateIntervalSeconds, ShouldEqual, 10)
|
||||
So(ds.UpdateIntervalSeconds, ShouldEqual, 15)
|
||||
|
||||
ds2 := cfg[1]
|
||||
So(ds2.Name, ShouldEqual, "default")
|
||||
@@ -81,5 +81,5 @@ func validateDashboardAsConfig(t *testing.T, cfg []*DashboardsAsConfig) {
|
||||
So(len(ds2.Options), ShouldEqual, 1)
|
||||
So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
||||
So(ds2.DisableDeletion, ShouldBeFalse)
|
||||
So(ds2.UpdateIntervalSeconds, ShouldEqual, 3)
|
||||
So(ds2.UpdateIntervalSeconds, ShouldEqual, 10)
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@ providers:
|
||||
folder: 'developers'
|
||||
editable: true
|
||||
disableDeletion: true
|
||||
updateIntervalSeconds: 10
|
||||
updateIntervalSeconds: 15
|
||||
type: file
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
folder: 'developers'
|
||||
editable: true
|
||||
disableDeletion: true
|
||||
updateIntervalSeconds: 10
|
||||
updateIntervalSeconds: 15
|
||||
type: file
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# org_id: 1
|
||||
|
||||
# # list of datasources to insert/update depending
|
||||
# # whats available in the datbase
|
||||
# # what's available in the database
|
||||
#datasources:
|
||||
# # <string, required> name of the datasource. Required
|
||||
# - name: Graphite
|
||||
|
||||
@@ -70,7 +70,7 @@ func (rs *RenderingService) renderViaHttp(ctx context.Context, opts Opts) (*Rend
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
||||
// if we didnt get a 200 response, something went wrong.
|
||||
// if we didn't get a 200 response, something went wrong.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
rs.log.Error("Remote rendering request failed", "error", resp.Status)
|
||||
return nil, fmt.Errorf("Remote rendering request failed. %d: %s", resp.StatusCode, resp.Status)
|
||||
@@ -83,7 +83,7 @@ func (rs *RenderingService) renderViaHttp(ctx context.Context, opts Opts) (*Rend
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
// check that we didnt timeout while receiving the response.
|
||||
// check that we didn't timeout while receiving the response.
|
||||
if reqContext.Err() == context.DeadlineExceeded {
|
||||
rs.log.Info("Rendering timed out")
|
||||
return nil, ErrTimeout
|
||||
|
||||
@@ -13,15 +13,16 @@ var ErrNoRenderer = errors.New("No renderer plugin found nor is an external rend
|
||||
var ErrPhantomJSNotInstalled = errors.New("PhantomJS executable not found")
|
||||
|
||||
type Opts struct {
|
||||
Width int
|
||||
Height int
|
||||
Timeout time.Duration
|
||||
OrgId int64
|
||||
UserId int64
|
||||
OrgRole models.RoleType
|
||||
Path string
|
||||
Encoding string
|
||||
Timezone string
|
||||
Width int
|
||||
Height int
|
||||
Timeout time.Duration
|
||||
OrgId int64
|
||||
UserId int64
|
||||
OrgRole models.RoleType
|
||||
Path string
|
||||
Encoding string
|
||||
Timezone string
|
||||
ConcurrentLimit int
|
||||
}
|
||||
|
||||
type RenderResult struct {
|
||||
|
||||
@@ -24,12 +24,13 @@ func init() {
|
||||
}
|
||||
|
||||
type RenderingService struct {
|
||||
log log.Logger
|
||||
pluginClient *plugin.Client
|
||||
grpcPlugin pluginModel.RendererPlugin
|
||||
pluginInfo *plugins.RendererPlugin
|
||||
renderAction renderFunc
|
||||
domain string
|
||||
log log.Logger
|
||||
pluginClient *plugin.Client
|
||||
grpcPlugin pluginModel.RendererPlugin
|
||||
pluginInfo *plugins.RendererPlugin
|
||||
renderAction renderFunc
|
||||
domain string
|
||||
inProgressCount int
|
||||
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
}
|
||||
@@ -45,7 +46,7 @@ func (rs *RenderingService) Init() error {
|
||||
|
||||
// set value used for domain attribute of renderKey cookie
|
||||
if rs.Cfg.RendererUrl != "" {
|
||||
// RendererCallbackUrl has already been passed, it wont generate an error.
|
||||
// RendererCallbackUrl has already been passed, it won't generate an error.
|
||||
u, _ := url.Parse(rs.Cfg.RendererCallbackUrl)
|
||||
rs.domain = u.Hostname()
|
||||
} else if setting.HttpAddr != setting.DEFAULT_HTTP_ADDR {
|
||||
@@ -90,6 +91,18 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResult, error) {
|
||||
if rs.inProgressCount > opts.ConcurrentLimit {
|
||||
return &RenderResult{
|
||||
FilePath: filepath.Join(setting.HomePath, "public/img/rendering_limit.png"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
rs.inProgressCount -= 1
|
||||
}()
|
||||
|
||||
rs.inProgressCount += 1
|
||||
|
||||
if rs.renderAction != nil {
|
||||
return rs.renderAction(ctx, opts)
|
||||
} else {
|
||||
|
||||
@@ -230,7 +230,7 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
|
||||
}
|
||||
|
||||
func RecordNotificationJournal(ctx context.Context, cmd *m.RecordNotificationJournalCommand) error {
|
||||
return inTransactionCtx(ctx, func(sess *DBSession) error {
|
||||
return withDbSession(ctx, func(sess *DBSession) error {
|
||||
journalEntry := &m.AlertNotificationJournal{
|
||||
OrgId: cmd.OrgId,
|
||||
AlertId: cmd.AlertId,
|
||||
@@ -245,21 +245,19 @@ func RecordNotificationJournal(ctx context.Context, cmd *m.RecordNotificationJou
|
||||
}
|
||||
|
||||
func GetLatestNotification(ctx context.Context, cmd *m.GetLatestNotificationQuery) error {
|
||||
return inTransactionCtx(ctx, func(sess *DBSession) error {
|
||||
nj := &m.AlertNotificationJournal{}
|
||||
return withDbSession(ctx, func(sess *DBSession) error {
|
||||
nj := []m.AlertNotificationJournal{}
|
||||
|
||||
_, err := sess.Desc("alert_notification_journal.sent_at").
|
||||
Limit(1).
|
||||
Where("alert_notification_journal.org_id = ? AND alert_notification_journal.alert_id = ? AND alert_notification_journal.notifier_id = ?", cmd.OrgId, cmd.AlertId, cmd.NotifierId).Get(nj)
|
||||
err := sess.Desc("alert_notification_journal.sent_at").
|
||||
Where("alert_notification_journal.org_id = ?", cmd.OrgId).
|
||||
Where("alert_notification_journal.alert_id = ?", cmd.AlertId).
|
||||
Where("alert_notification_journal.notifier_id = ?", cmd.NotifierId).
|
||||
Find(&nj)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nj.AlertId == 0 && nj.Id == 0 && nj.NotifierId == 0 && nj.OrgId == 0 {
|
||||
return m.ErrJournalingNotFound
|
||||
}
|
||||
|
||||
cmd.Result = nj
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -15,16 +15,21 @@ func TestAlertNotificationSQLAccess(t *testing.T) {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Alert notification journal", func() {
|
||||
var alertId int64 = 5
|
||||
var alertId int64 = 7
|
||||
var orgId int64 = 5
|
||||
var notifierId int64 = 5
|
||||
var notifierId int64 = 10
|
||||
|
||||
Convey("Getting last journal should raise error if no one exists", func() {
|
||||
query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId}
|
||||
err := GetLatestNotification(context.Background(), query)
|
||||
So(err, ShouldEqual, m.ErrJournalingNotFound)
|
||||
GetLatestNotification(context.Background(), query)
|
||||
So(len(query.Result), ShouldEqual, 0)
|
||||
|
||||
Convey("shoulbe be able to record two journaling events", func() {
|
||||
// recording an journal entry in another org to make sure org filter works as expected.
|
||||
journalInOtherOrg := &m.RecordNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: 10, Success: true, SentAt: 1}
|
||||
err := RecordNotificationJournal(context.Background(), journalInOtherOrg)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("should be able to record two journaling events", func() {
|
||||
createCmd := &m.RecordNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId, Success: true, SentAt: 1}
|
||||
|
||||
err := RecordNotificationJournal(context.Background(), createCmd)
|
||||
@@ -38,17 +43,20 @@ func TestAlertNotificationSQLAccess(t *testing.T) {
|
||||
Convey("get last journaling event", func() {
|
||||
err := GetLatestNotification(context.Background(), query)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.SentAt, ShouldEqual, 1001)
|
||||
So(len(query.Result), ShouldEqual, 2)
|
||||
last := query.Result[0]
|
||||
So(last.SentAt, ShouldEqual, 1001)
|
||||
|
||||
Convey("be able to clear all journaling for an notifier", func() {
|
||||
cmd := &m.CleanNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId}
|
||||
err := CleanNotificationJournal(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("querying for last junaling should raise error", func() {
|
||||
Convey("querying for last journaling should return no journal entries", func() {
|
||||
query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId}
|
||||
err := GetLatestNotification(context.Background(), query)
|
||||
So(err, ShouldEqual, m.ErrJournalingNotFound)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -105,7 +105,7 @@ func addAnnotationMig(mg *Migrator) {
|
||||
}))
|
||||
|
||||
//
|
||||
// Convert epoch saved as seconds to miliseconds
|
||||
// Convert epoch saved as seconds to milliseconds
|
||||
//
|
||||
updateEpochSql := "UPDATE annotation SET epoch = (epoch*1000) where epoch < 9999999999"
|
||||
mg.AddMigration("Convert existing annotations from seconds to milliseconds", NewRawSqlMigration(updateEpochSql))
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestTransaction(t *testing.T) {
|
||||
So(err, ShouldEqual, models.ErrInvalidApiKey)
|
||||
})
|
||||
|
||||
Convey("wont update if one handler fails", func() {
|
||||
Convey("won't update if one handler fails", func() {
|
||||
err := ss.InTransaction(context.Background(), func(ctx context.Context) error {
|
||||
err := DeleteApiKeyCtx(ctx, deleteApiKeyCmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -271,9 +271,6 @@ func ChangeUserPassword(cmd *m.ChangeUserPasswordCommand) error {
|
||||
|
||||
func UpdateUserLastSeenAt(cmd *m.UpdateUserLastSeenAtCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if cmd.UserId <= 0 {
|
||||
}
|
||||
|
||||
user := m.User{
|
||||
Id: cmd.UserId,
|
||||
LastSeenAt: time.Now(),
|
||||
|
||||
@@ -166,6 +166,7 @@ var (
|
||||
// Alerting
|
||||
AlertingEnabled bool
|
||||
ExecuteAlerts bool
|
||||
AlertingRenderLimit int
|
||||
AlertingErrorOrTimeout string
|
||||
AlertingNoDataOrNullValues string
|
||||
|
||||
@@ -196,10 +197,13 @@ type Cfg struct {
|
||||
Smtp SmtpSettings
|
||||
|
||||
// Rendering
|
||||
ImagesDir string
|
||||
PhantomDir string
|
||||
RendererUrl string
|
||||
RendererCallbackUrl string
|
||||
ImagesDir string
|
||||
PhantomDir string
|
||||
RendererUrl string
|
||||
RendererCallbackUrl string
|
||||
RendererLimit int
|
||||
RendererLimitAlerting int
|
||||
|
||||
DisableBruteForceLoginProtection bool
|
||||
|
||||
TempDataLifetime time.Duration
|
||||
@@ -677,6 +681,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
alerting := iniFile.Section("alerting")
|
||||
AlertingEnabled = alerting.Key("enabled").MustBool(true)
|
||||
ExecuteAlerts = alerting.Key("execute_alerts").MustBool(true)
|
||||
AlertingRenderLimit = alerting.Key("concurrent_render_limit").MustInt(5)
|
||||
AlertingErrorOrTimeout = alerting.Key("error_or_timeout").MustString("alerting")
|
||||
AlertingNoDataOrNullValues = alerting.Key("nodata_or_nullvalues").MustString("no_data")
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ func (ts *TracingService) parseSettings() {
|
||||
|
||||
func (ts *TracingService) initGlobalTracer() error {
|
||||
cfg := jaegercfg.Configuration{
|
||||
Disabled: !ts.enabled,
|
||||
ServiceName: "grafana",
|
||||
Disabled: !ts.enabled,
|
||||
Sampler: &jaegercfg.SamplerConfig{
|
||||
Type: ts.samplerType,
|
||||
Param: ts.samplerParam,
|
||||
@@ -78,7 +79,7 @@ func (ts *TracingService) initGlobalTracer() error {
|
||||
options = append(options, jaegercfg.Tag(tag, value))
|
||||
}
|
||||
|
||||
tracer, closer, err := cfg.New("grafana", options...)
|
||||
tracer, closer, err := cfg.NewTracer(options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ func (e *CloudWatchExecutor) executeQuery(ctx context.Context, query *CloudWatch
|
||||
params.ExtendedStatistics = query.ExtendedStatistics
|
||||
}
|
||||
|
||||
// 1 minutes resolutin metrics is stored for 15 days, 15 * 24 * 60 = 21600
|
||||
// 1 minutes resolution metrics is stored for 15 days, 15 * 24 * 60 = 21600
|
||||
if query.HighResolution && (((endTime.Unix() - startTime.Unix()) / int64(query.Period)) > 21600) {
|
||||
return nil, errors.New("too long query period")
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func (e *CloudWatchExecutor) executeGetMetricDataQuery(ctx context.Context, regi
|
||||
ScanBy: aws.String("TimestampAscending"),
|
||||
}
|
||||
for _, query := range queries {
|
||||
// 1 minutes resolutin metrics is stored for 15 days, 15 * 24 * 60 = 21600
|
||||
// 1 minutes resolution metrics is stored for 15 days, 15 * 24 * 60 = 21600
|
||||
if query.HighResolution && (((endTime.Unix() - startTime.Unix()) / int64(query.Period)) > 21600) {
|
||||
return nil, errors.New("too long query period")
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestClient(t *testing.T) {
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("When unspported version set should return error", func() {
|
||||
Convey("When unsupported version set should return error", func() {
|
||||
ds := &models.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
"esVersion": 6,
|
||||
|
||||
@@ -56,9 +56,7 @@ func (b *SearchRequestBuilder) Build() (*SearchRequest, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, agg := range aggArray {
|
||||
sr.Aggs = append(sr.Aggs, agg)
|
||||
}
|
||||
sr.Aggs = append(sr.Aggs, aggArray...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +110,7 @@ func (b *SearchRequestBuilder) Query() *QueryBuilder {
|
||||
return b.queryBuilder
|
||||
}
|
||||
|
||||
// Agg initaite and returns a new aggregation builder
|
||||
// Agg initiate and returns a new aggregation builder
|
||||
func (b *SearchRequestBuilder) Agg() AggBuilder {
|
||||
aggBuilder := newAggBuilder()
|
||||
b.aggBuilders = append(b.aggBuilders, aggBuilder)
|
||||
@@ -300,9 +298,7 @@ func (b *aggBuilderImpl) Build() (AggArray, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, childAgg := range childAggs {
|
||||
agg.Aggregation.Aggs = append(agg.Aggregation.Aggs, childAgg)
|
||||
}
|
||||
agg.Aggregation.Aggs = append(agg.Aggregation.Aggs, childAggs...)
|
||||
}
|
||||
|
||||
aggs = append(aggs, agg)
|
||||
|
||||
@@ -92,7 +92,7 @@ func (rp *responseParser) processBuckets(aggs map[string]interface{}, target *Qu
|
||||
} else {
|
||||
for _, b := range esAgg.Get("buckets").MustArray() {
|
||||
bucket := simplejson.NewFromAny(b)
|
||||
newProps := make(map[string]string, 0)
|
||||
newProps := make(map[string]string)
|
||||
|
||||
for k, v := range props {
|
||||
newProps[k] = v
|
||||
@@ -122,7 +122,7 @@ func (rp *responseParser) processBuckets(aggs map[string]interface{}, target *Qu
|
||||
|
||||
for _, bucketKey := range bucketKeys {
|
||||
bucket := simplejson.NewFromAny(buckets[bucketKey])
|
||||
newProps := make(map[string]string, 0)
|
||||
newProps := make(map[string]string)
|
||||
|
||||
for k, v := range props {
|
||||
newProps[k] = v
|
||||
@@ -314,7 +314,6 @@ func (rp *responseParser) processAggregationDocs(esAgg *simplejson.Json, aggDef
|
||||
switch metric.Type {
|
||||
case "count":
|
||||
addMetricValue(&values, rp.getMetricName(metric.Type), castToNullFloat(bucket.Get("doc_count")))
|
||||
break
|
||||
case "extended_stats":
|
||||
metaKeys := make([]string, 0)
|
||||
meta := metric.Meta.MustMap()
|
||||
@@ -355,7 +354,6 @@ func (rp *responseParser) processAggregationDocs(esAgg *simplejson.Json, aggDef
|
||||
}
|
||||
|
||||
addMetricValue(&values, metricName, castToNullFloat(bucket.GetPath(metric.ID, "value")))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
|
||||
So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" < 10001`)
|
||||
})
|
||||
|
||||
Convey("can render number greather then condition tags", func() {
|
||||
Convey("can render number greater then condition tags", func() {
|
||||
query := &Query{Tags: []*Tag{{Operator: ">", Value: "10001", Key: "key"}}}
|
||||
|
||||
So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" > 10001`)
|
||||
|
||||
@@ -92,12 +92,12 @@ func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSourc
|
||||
return nil, err
|
||||
}
|
||||
|
||||
querys, err := parseQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
|
||||
queries, err := parseQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, query := range querys {
|
||||
for _, query := range queries {
|
||||
timeRange := apiv1.Range{
|
||||
Start: query.Start,
|
||||
End: query.End,
|
||||
|
||||
@@ -3,14 +3,14 @@ package util
|
||||
import "testing"
|
||||
|
||||
func TestMd5Sum(t *testing.T) {
|
||||
input := "dont hash passwords with md5"
|
||||
input := "don't hash passwords with md5"
|
||||
|
||||
have, err := Md5SumString(input)
|
||||
if err != nil {
|
||||
t.Fatal("expected err to be nil")
|
||||
}
|
||||
|
||||
want := "2d6a56c82d09d374643b926d3417afba"
|
||||
want := "dd1f7fdb3466c0d09c2e839d1f1530f8"
|
||||
if have != want {
|
||||
t.Fatalf("expected: %s got: %s", want, have)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user