mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'origin/master' into develop
This commit is contained in:
@@ -51,6 +51,8 @@ From `/etc/grafana/datasources` to `/etc/grafana/provisioning/datasources` when
|
|||||||
## Fixes
|
## Fixes
|
||||||
* **Gzip**: Fixes bug gravatar images when gzip was enabled [#5952](https://github.com/grafana/grafana/issues/5952)
|
* **Gzip**: Fixes bug gravatar images when gzip was enabled [#5952](https://github.com/grafana/grafana/issues/5952)
|
||||||
* **Alert list**: Now shows alert state changes even after adding manual annotations on dashboard [#9951](https://github.com/grafana/grafana/issues/9951)
|
* **Alert list**: Now shows alert state changes even after adding manual annotations on dashboard [#9951](https://github.com/grafana/grafana/issues/9951)
|
||||||
|
* **Alerting**: Fixes bug where rules evaluated as firing when all conditions was false and using OR operator. [#9318](https://github.com/grafana/grafana/issues/9318)
|
||||||
|
* **Cloudwatch**: CloudWatch no longer display metrics' default alias [#10151](https://github.com/grafana/grafana/issues/10151), thx [@mtanda](https://github.com/mtanda)
|
||||||
|
|
||||||
# 4.6.2 (2017-11-16)
|
# 4.6.2 (2017-11-16)
|
||||||
|
|
||||||
|
|||||||
@@ -221,6 +221,9 @@ external_manage_link_url =
|
|||||||
external_manage_link_name =
|
external_manage_link_name =
|
||||||
external_manage_info =
|
external_manage_info =
|
||||||
|
|
||||||
|
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
|
||||||
|
viewers_can_edit = false
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
# Set to true to disable (hide) the login form, useful if you use OAuth
|
# Set to true to disable (hide) the login form, useful if you use OAuth
|
||||||
disable_login_form = false
|
disable_login_form = false
|
||||||
|
|||||||
@@ -205,6 +205,9 @@ log_queries =
|
|||||||
;external_manage_link_name =
|
;external_manage_link_name =
|
||||||
;external_manage_info =
|
;external_manage_info =
|
||||||
|
|
||||||
|
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
|
||||||
|
;viewers_can_edit = false
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
|
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
|
||||||
;disable_login_form = false
|
;disable_login_form = false
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
graphite:
|
graphite09:
|
||||||
build: blocks/graphite
|
build: blocks/graphite
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
|||||||
|
|
||||||
**Example Request**:
|
**Example Request**:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
DELETE /api/auth/keys/3 HTTP/1.1
|
DELETE /api/auth/keys/3 HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ The database user (not applicable for `sqlite3`).
|
|||||||
|
|
||||||
### password
|
### password
|
||||||
|
|
||||||
The database user's password (not applicable for `sqlite3`). If the password contains `#` or `;` you have to wrap it with trippel quotes. Ex `"""#password;"""`
|
The database user's password (not applicable for `sqlite3`). If the password contains `#` or `;` you have to wrap it with triple quotes. Ex `"""#password;"""`
|
||||||
|
|
||||||
### ssl_mode
|
### ssl_mode
|
||||||
|
|
||||||
@@ -214,19 +214,19 @@ For MySQL, use either `true`, `false`, or `skip-verify`.
|
|||||||
|
|
||||||
### ca_cert_path
|
### ca_cert_path
|
||||||
|
|
||||||
(MySQL only) The path to the CA certificate to use. On many linux systems, certs can be found in `/etc/ssl/certs`.
|
The path to the CA certificate to use. On many linux systems, certs can be found in `/etc/ssl/certs`.
|
||||||
|
|
||||||
### client_key_path
|
### client_key_path
|
||||||
|
|
||||||
(MySQL only) The path to the client key. Only if server requires client authentication.
|
The path to the client key. Only if server requires client authentication.
|
||||||
|
|
||||||
### client_cert_path
|
### client_cert_path
|
||||||
|
|
||||||
(MySQL only) The path to the client cert. Only if server requires client authentication.
|
The path to the client cert. Only if server requires client authentication.
|
||||||
|
|
||||||
### server_cert_name
|
### server_cert_name
|
||||||
|
|
||||||
(MySQL only) The common name field of the certificate used by the `mysql` server. Not necessary if `ssl_mode` is set to `skip-verify`.
|
The common name field of the certificate used by the `mysql` or `postgres` server. Not necessary if `ssl_mode` is set to `skip-verify`.
|
||||||
|
|
||||||
### max_idle_conn
|
### max_idle_conn
|
||||||
The maximum number of connections in the idle connection pool.
|
The maximum number of connections in the idle connection pool.
|
||||||
@@ -292,10 +292,14 @@ organization to be created for that new user.
|
|||||||
|
|
||||||
The role new users will be assigned for the main organization (if the
|
The role new users will be assigned for the main organization (if the
|
||||||
above setting is set to true). Defaults to `Viewer`, other valid
|
above setting is set to true). Defaults to `Viewer`, other valid
|
||||||
options are `Admin` and `Editor` and `Read Only Editor`. e.g. :
|
options are `Admin` and `Editor`. e.g. :
|
||||||
|
|
||||||
`auto_assign_org_role = Read Only Editor`
|
`auto_assign_org_role = Viewer`
|
||||||
|
|
||||||
|
### viewers can edit
|
||||||
|
|
||||||
|
Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
|
||||||
|
Defaults to `false`.
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|||||||
@@ -229,10 +229,6 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
|
|||||||
return Json(200, util.DynMap{"status": "success", "slug": dashboard.Slug, "version": dashboard.Version, "id": dashboard.Id})
|
return Json(200, util.DynMap{"status": "success", "slug": dashboard.Slug, "version": dashboard.Version, "id": dashboard.Id})
|
||||||
}
|
}
|
||||||
|
|
||||||
func canEditDashboard(role m.RoleType) bool {
|
|
||||||
return role == m.ROLE_ADMIN || role == m.ROLE_EDITOR || role == m.ROLE_READ_ONLY_EDITOR
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetHomeDashboard(c *middleware.Context) Response {
|
func GetHomeDashboard(c *middleware.Context) Response {
|
||||||
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
|
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
|
||||||
if err := bus.Dispatch(&prefsQuery); err != nil {
|
if err := bus.Dispatch(&prefsQuery); err != nil {
|
||||||
@@ -258,7 +254,7 @@ func GetHomeDashboard(c *middleware.Context) Response {
|
|||||||
|
|
||||||
dash := dtos.DashboardFullWithMeta{}
|
dash := dtos.DashboardFullWithMeta{}
|
||||||
dash.Meta.IsHome = true
|
dash.Meta.IsHome = true
|
||||||
dash.Meta.CanEdit = c.SignedInUser.HasRole(m.ROLE_READ_ONLY_EDITOR)
|
dash.Meta.CanEdit = c.SignedInUser.HasRole(m.ROLE_EDITOR)
|
||||||
dash.Meta.FolderTitle = "Root"
|
dash.Meta.FolderTitle = "Root"
|
||||||
|
|
||||||
jsonParser := json.NewDecoder(file)
|
jsonParser := json.NewDecoder(file)
|
||||||
|
|||||||
@@ -18,14 +18,13 @@ var (
|
|||||||
type RoleType string
|
type RoleType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ROLE_VIEWER RoleType = "Viewer"
|
ROLE_VIEWER RoleType = "Viewer"
|
||||||
ROLE_EDITOR RoleType = "Editor"
|
ROLE_EDITOR RoleType = "Editor"
|
||||||
ROLE_READ_ONLY_EDITOR RoleType = "Read Only Editor"
|
ROLE_ADMIN RoleType = "Admin"
|
||||||
ROLE_ADMIN RoleType = "Admin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r RoleType) IsValid() bool {
|
func (r RoleType) IsValid() bool {
|
||||||
return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR
|
return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoleType) Includes(other RoleType) bool {
|
func (r RoleType) Includes(other RoleType) bool {
|
||||||
@@ -33,16 +32,8 @@ func (r RoleType) Includes(other RoleType) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if other == ROLE_READ_ONLY_EDITOR {
|
if r == ROLE_EDITOR {
|
||||||
return r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR
|
return other != ROLE_ADMIN
|
||||||
}
|
|
||||||
|
|
||||||
if other == ROLE_EDITOR {
|
|
||||||
return r == ROLE_EDITOR
|
|
||||||
}
|
|
||||||
|
|
||||||
if other == ROLE_VIEWER {
|
|
||||||
return r == ROLE_READ_ONLY_EDITOR || r == ROLE_EDITOR || r == ROLE_VIEWER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -75,14 +75,6 @@ func (c *EvalContext) ShouldUpdateAlertState() bool {
|
|||||||
return c.Rule.State != c.PrevAlertState
|
return c.Rule.State != c.PrevAlertState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EvalContext) ShouldSendNotification() bool {
|
|
||||||
if (c.PrevAlertState == m.AlertStatePending) && (c.Rule.State == m.AlertStateOK) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *EvalContext) GetDurationMs() float64 {
|
func (a *EvalContext) GetDurationMs() float64 {
|
||||||
return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000)
|
return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,21 +28,5 @@ func TestAlertingEvalContext(t *testing.T) {
|
|||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should send notifications", func() {
|
|
||||||
Convey("pending -> ok", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStatePending
|
|
||||||
ctx.Rule.State = models.AlertStateOK
|
|
||||||
|
|
||||||
So(ctx.ShouldSendNotification(), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("ok -> alerting", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Rule.State = models.AlertStateAlerting
|
|
||||||
|
|
||||||
So(ctx.ShouldSendNotification(), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
firing = cr.Firing
|
||||||
|
noDataFound = cr.NoDataFound
|
||||||
|
}
|
||||||
|
|
||||||
// calculating Firing based on operator
|
// calculating Firing based on operator
|
||||||
if cr.Operator == "or" {
|
if cr.Operator == "or" {
|
||||||
firing = firing || cr.Firing
|
firing = firing || cr.Firing
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
So(context.ConditionEvals, ShouldEqual, "true = true")
|
So(context.ConditionEvals, ShouldEqual, "true = true")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Show return triggered with single passing condition2", func() {
|
||||||
|
context := NewEvalContext(context.TODO(), &Rule{
|
||||||
|
Conditions: []Condition{&conditionStub{firing: true, operator: "and"}},
|
||||||
|
})
|
||||||
|
|
||||||
|
handler.Eval(context)
|
||||||
|
So(context.Firing, ShouldEqual, true)
|
||||||
|
So(context.ConditionEvals, ShouldEqual, "true = true")
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Show return false with not passing asdf", func() {
|
Convey("Show return false with not passing asdf", func() {
|
||||||
context := NewEvalContext(context.TODO(), &Rule{
|
context := NewEvalContext(context.TODO(), &Rule{
|
||||||
Conditions: []Condition{
|
Conditions: []Condition{
|
||||||
@@ -131,6 +141,33 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true")
|
So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Should return false if no condition is firing using OR operator", func() {
|
||||||
|
context := NewEvalContext(context.TODO(), &Rule{
|
||||||
|
Conditions: []Condition{
|
||||||
|
&conditionStub{firing: false, operator: "or"},
|
||||||
|
&conditionStub{firing: false, operator: "or"},
|
||||||
|
&conditionStub{firing: false, operator: "or"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
handler.Eval(context)
|
||||||
|
So(context.Firing, ShouldEqual, false)
|
||||||
|
So(context.ConditionEvals, ShouldEqual, "[[false OR false] OR false] = false")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should retuasdfrn no data if one condition has nodata", func() {
|
||||||
|
context := NewEvalContext(context.TODO(), &Rule{
|
||||||
|
Conditions: []Condition{
|
||||||
|
&conditionStub{operator: "or", noData: false},
|
||||||
|
&conditionStub{operator: "or", noData: false},
|
||||||
|
&conditionStub{operator: "or", noData: false},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
handler.Eval(context)
|
||||||
|
So(context.NoDataFound, ShouldBeFalse)
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Should return no data if one condition has nodata", func() {
|
Convey("Should return no data if one condition has nodata", func() {
|
||||||
context := NewEvalContext(context.TODO(), &Rule{
|
context := NewEvalContext(context.TODO(), &Rule{
|
||||||
Conditions: []Condition{
|
Conditions: []Condition{
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Notifier interface {
|
|||||||
Notify(evalContext *EvalContext) error
|
Notify(evalContext *EvalContext) error
|
||||||
GetType() string
|
GetType() string
|
||||||
NeedsImage() bool
|
NeedsImage() bool
|
||||||
PassesFilter(rule *Rule) bool
|
ShouldNotify(evalContext *EvalContext) bool
|
||||||
|
|
||||||
GetNotifierId() int64
|
GetNotifierId() int64
|
||||||
GetIsDefault() bool
|
GetIsDefault() bool
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ type NotifierPlugin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotificationService interface {
|
type NotificationService interface {
|
||||||
Send(context *EvalContext) error
|
SendIfNeeded(context *EvalContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNotificationService() NotificationService {
|
func NewNotificationService() NotificationService {
|
||||||
@@ -41,14 +41,12 @@ func newNotificationService() *notificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationService) Send(context *EvalContext) error {
|
func (n *notificationService) SendIfNeeded(context *EvalContext) error {
|
||||||
notifiers, err := n.getNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
|
notifiers, err := n.getNeededNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
n.log.Info("Sending notifications for", "ruleId", context.Rule.Id, "sent count", len(notifiers))
|
|
||||||
|
|
||||||
if len(notifiers) == 0 {
|
if len(notifiers) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -110,7 +108,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationService) getNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
|
func (n *notificationService) getNeededNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
|
||||||
query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}
|
query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}
|
||||||
|
|
||||||
if err := bus.Dispatch(query); err != nil {
|
if err := bus.Dispatch(query); err != nil {
|
||||||
@@ -122,7 +120,7 @@ func (n *notificationService) getNotifiers(orgId int64, notificationIds []int64,
|
|||||||
if not, err := n.createNotifierFor(notification); err != nil {
|
if not, err := n.createNotifierFor(notification); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
if shouldUseNotification(not, context) {
|
if not.ShouldNotify(context) {
|
||||||
result = append(result, not)
|
result = append(result, not)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,18 +138,6 @@ func (n *notificationService) createNotifierFor(model *m.AlertNotification) (Not
|
|||||||
return notifierPlugin.Factory(model)
|
return notifierPlugin.Factory(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldUseNotification(notifier Notifier, context *EvalContext) bool {
|
|
||||||
if !context.Firing {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Error != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return notifier.PassesFilter(context.Rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)
|
type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)
|
||||||
|
|
||||||
var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)
|
var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
package alerting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FakeNotifier struct {
|
|
||||||
FakeMatchResult bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fn *FakeNotifier) GetType() string {
|
|
||||||
return "FakeNotifier"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fn *FakeNotifier) NeedsImage() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *FakeNotifier) GetNotifierId() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *FakeNotifier) GetIsDefault() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fn *FakeNotifier) Notify(alertResult *EvalContext) error { return nil }
|
|
||||||
|
|
||||||
func (fn *FakeNotifier) PassesFilter(rule *Rule) bool {
|
|
||||||
return fn.FakeMatchResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAlertNotificationExtraction(t *testing.T) {
|
|
||||||
|
|
||||||
Convey("Notifier tests", t, func() {
|
|
||||||
Convey("none firing alerts", func() {
|
|
||||||
ctx := &EvalContext{
|
|
||||||
Firing: false,
|
|
||||||
Rule: &Rule{
|
|
||||||
State: m.AlertStateAlerting,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
notifier := &FakeNotifier{FakeMatchResult: false}
|
|
||||||
|
|
||||||
So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("execution error cannot be ignored", func() {
|
|
||||||
ctx := &EvalContext{
|
|
||||||
Firing: true,
|
|
||||||
Error: fmt.Errorf("I used to be a programmer just like you"),
|
|
||||||
Rule: &Rule{
|
|
||||||
State: m.AlertStateOK,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
notifier := &FakeNotifier{FakeMatchResult: false}
|
|
||||||
|
|
||||||
So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("firing alert that match", func() {
|
|
||||||
ctx := &EvalContext{
|
|
||||||
Firing: true,
|
|
||||||
Rule: &Rule{
|
|
||||||
State: models.AlertStateAlerting,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
notifier := &FakeNotifier{FakeMatchResult: true}
|
|
||||||
|
|
||||||
So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("firing alert that dont match", func() {
|
|
||||||
ctx := &EvalContext{
|
|
||||||
Firing: true,
|
|
||||||
Rule: &Rule{State: m.AlertStateOK},
|
|
||||||
}
|
|
||||||
notifier := &FakeNotifier{FakeMatchResult: false}
|
|
||||||
|
|
||||||
So(shouldUseNotification(notifier, ctx), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package notifiers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +26,13 @@ func NewNotifierBase(id int64, isDefault bool, name, notifierType string, model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierBase) PassesFilter(rule *alerting.Rule) bool {
|
func defaultShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
if context.PrevAlertState == context.Rule.State {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (context.PrevAlertState == m.AlertStatePending) && (context.Rule.State == m.AlertStateOK) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
pkg/services/alerting/notifiers/base_test.go
Normal file
32
pkg/services/alerting/notifiers/base_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaseNotifier(t *testing.T) {
|
||||||
|
Convey("Base notifier tests", t, func() {
|
||||||
|
Convey("should notify", func() {
|
||||||
|
Convey("pending -> ok", func() {
|
||||||
|
context := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
|
||||||
|
State: m.AlertStatePending,
|
||||||
|
})
|
||||||
|
context.Rule.State = m.AlertStateOK
|
||||||
|
So(defaultShouldNotify(context), ShouldBeFalse)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("ok -> alerting", func() {
|
||||||
|
context := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
|
||||||
|
State: m.AlertStateOK,
|
||||||
|
})
|
||||||
|
context.Rule.State = m.AlertStateAlerting
|
||||||
|
So(defaultShouldNotify(context), ShouldBeTrue)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -38,6 +38,10 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *DingDingNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
type DingDingNotifier struct {
|
type DingDingNotifier struct {
|
||||||
NotifierBase
|
NotifierBase
|
||||||
Url string
|
Url string
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDingDingNotifier(t *testing.T) {
|
func TestDingDingNotifier(t *testing.T) {
|
||||||
Convey("Line notifier tests", t, func() {
|
Convey("Dingding notifier tests", t, func() {
|
||||||
Convey("empty settings should return error", func() {
|
Convey("empty settings should return error", func() {
|
||||||
json := `{ }`
|
json := `{ }`
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *EmailNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Sending alert notification to", "addresses", this.Addresses)
|
this.log.Info("Sending alert notification to", "addresses", this.Addresses)
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ type HipChatNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HipChatNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ type KafkaNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *KafkaNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
|
|
||||||
state := evalContext.Rule.State
|
state := evalContext.Rule.State
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ type LineNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *LineNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
this.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ type OpsGenieNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *OpsGenieNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ type PagerdutyNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *PagerdutyNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
|
|
||||||
if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
|
if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ type PushoverNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *PushoverNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
ruleUrl, err := evalContext.GetRuleUrl()
|
ruleUrl, err := evalContext.GetRuleUrl()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ type SensuNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *SensuNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Sending sensu result")
|
this.log.Info("Sending sensu result")
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,10 @@ type SlackNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *SlackNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
this.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ func TestSlackNotifier(t *testing.T) {
|
|||||||
So(slackNotifier.Mention, ShouldEqual, "@carl")
|
So(slackNotifier.Mention, ShouldEqual, "@carl")
|
||||||
So(slackNotifier.Token, ShouldEqual, "xoxb-XXXXXXXX-XXXXXXXX-XXXXXXXXXX")
|
So(slackNotifier.Token, ShouldEqual, "xoxb-XXXXXXXX-XXXXXXXX-XXXXXXXXXX")
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ type TeamsNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *TeamsNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *TelegramNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Sending alert notification to", "bot_token", this.BotToken)
|
this.log.Info("Sending alert notification to", "bot_token", this.BotToken)
|
||||||
this.log.Info("Sending alert notification to", "chat_id", this.ChatID)
|
this.log.Info("Sending alert notification to", "chat_id", this.ChatID)
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ func NewThreemaNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ThreemaNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
notifier.log.Info("Sending alert notification from", "threema_id", notifier.GatewayID)
|
notifier.log.Info("Sending alert notification from", "threema_id", notifier.GatewayID)
|
||||||
notifier.log.Info("Sending alert notification to", "threema_id", notifier.RecipientID)
|
notifier.log.Info("Sending alert notification to", "threema_id", notifier.RecipientID)
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ type VictoropsNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *VictoropsNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
// Notify sends notification to Victorops via POST to URL endpoint
|
// Notify sends notification to Victorops via POST to URL endpoint
|
||||||
func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
this.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ type WebhookNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *WebhookNotifier) ShouldNotify(context *alerting.EvalContext) bool {
|
||||||
|
return defaultShouldNotify(context)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||||
this.log.Info("Sending webhook")
|
this.log.Info("Sending webhook")
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func TestWebhookNotifier(t *testing.T) {
|
|||||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||||
model := &m.AlertNotification{
|
model := &m.AlertNotification{
|
||||||
Name: "ops",
|
Name: "ops",
|
||||||
Type: "email",
|
Type: "webhook",
|
||||||
Settings: settingsJSON,
|
Settings: settingsJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ func TestWebhookNotifier(t *testing.T) {
|
|||||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||||
model := &m.AlertNotification{
|
model := &m.AlertNotification{
|
||||||
Name: "ops",
|
Name: "ops",
|
||||||
Type: "email",
|
Type: "webhook",
|
||||||
Settings: settingsJSON,
|
Settings: settingsJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ func TestWebhookNotifier(t *testing.T) {
|
|||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(webhookNotifier.Name, ShouldEqual, "ops")
|
So(webhookNotifier.Name, ShouldEqual, "ops")
|
||||||
So(webhookNotifier.Type, ShouldEqual, "email")
|
So(webhookNotifier.Type, ShouldEqual, "webhook")
|
||||||
So(webhookNotifier.Url, ShouldEqual, "http://google.com")
|
So(webhookNotifier.Url, ShouldEqual, "http://google.com")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -85,11 +85,9 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
|||||||
if err := annotationRepo.Save(&item); err != nil {
|
if err := annotationRepo.Save(&item); err != nil {
|
||||||
handler.log.Error("Failed to save annotation for new alert state", "error", err)
|
handler.log.Error("Failed to save annotation for new alert state", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if evalContext.ShouldSendNotification() {
|
|
||||||
handler.notifier.Send(evalContext)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler.notifier.SendIfNeeded(evalContext)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
orgRole := g.user.OrgRole
|
orgRole := g.user.OrgRole
|
||||||
if orgRole == m.ROLE_READ_ONLY_EDITOR {
|
|
||||||
orgRole = m.ROLE_VIEWER
|
|
||||||
}
|
|
||||||
|
|
||||||
teamAclItems := []*m.DashboardAclInfoDTO{}
|
teamAclItems := []*m.DashboardAclInfoDTO{}
|
||||||
|
|
||||||
for _, p := range acl {
|
for _, p := range acl {
|
||||||
|
|||||||
@@ -83,4 +83,10 @@ func addOrgMigrations(mg *Migrator) {
|
|||||||
mg.AddMigration("Update org_user table charset", NewTableCharsetMigration("org_user", []*Column{
|
mg.AddMigration("Update org_user table charset", NewTableCharsetMigration("org_user", []*Column{
|
||||||
{Name: "role", Type: DB_NVarchar, Length: 20},
|
{Name: "role", Type: DB_NVarchar, Length: 20},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const migrateReadOnlyViewersToViewers = `UPDATE org_user SET role = 'Viewer' WHERE role = 'Read Only Editor'`
|
||||||
|
mg.AddMigration("Migrate all Read Only Viewers to Viewers", new(RawSqlMigration).
|
||||||
|
Sqlite(migrateReadOnlyViewersToViewers).
|
||||||
|
Postgres(migrateReadOnlyViewersToViewers).
|
||||||
|
Mysql(migrateReadOnlyViewersToViewers))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ var (
|
|||||||
ExternalUserMngLinkUrl string
|
ExternalUserMngLinkUrl string
|
||||||
ExternalUserMngLinkName string
|
ExternalUserMngLinkName string
|
||||||
ExternalUserMngInfo string
|
ExternalUserMngInfo string
|
||||||
|
ViewersCanEdit bool
|
||||||
|
|
||||||
// Http auth
|
// Http auth
|
||||||
AdminUser string
|
AdminUser string
|
||||||
@@ -540,13 +541,14 @@ func NewConfigContext(args *CommandLineArgs) error {
|
|||||||
AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
|
AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
|
||||||
AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
|
AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
|
||||||
AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
|
AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
|
||||||
AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Read Only Editor", "Viewer"})
|
AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
|
||||||
VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
|
VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
|
||||||
LoginHint = users.Key("login_hint").String()
|
LoginHint = users.Key("login_hint").String()
|
||||||
DefaultTheme = users.Key("default_theme").String()
|
DefaultTheme = users.Key("default_theme").String()
|
||||||
ExternalUserMngLinkUrl = users.Key("external_manage_link_url").String()
|
ExternalUserMngLinkUrl = users.Key("external_manage_link_url").String()
|
||||||
ExternalUserMngLinkName = users.Key("external_manage_link_name").String()
|
ExternalUserMngLinkName = users.Key("external_manage_link_name").String()
|
||||||
ExternalUserMngInfo = users.Key("external_manage_info").String()
|
ExternalUserMngInfo = users.Key("external_manage_info").String()
|
||||||
|
ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false)
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
auth := Cfg.Section("auth")
|
auth := Cfg.Section("auth")
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-select-wrapper">
|
<span class="gf-form-select-wrapper">
|
||||||
<select type="text" ng-model="orgUser.role" class="gf-form-input max-width-8" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
|
<select type="text" ng-model="orgUser.role" class="gf-form-input max-width-8" ng-options="f for f in ['Viewer', 'Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,10 +59,10 @@
|
|||||||
<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs" required class="gf-form-input max-width-20" placeholder="organization name">
|
<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs" required class="gf-form-input max-width-20" placeholder="organization name">
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label">Role</span>
|
<span class="gf-form-label">Role</span>
|
||||||
<span class="gf-form-select-wrapper">
|
<span class="gf-form-select-wrapper">
|
||||||
<select type="text" ng-model="newOrg.role" class="gf-form-input width-10" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']"></select>
|
<select type="text" ng-model="newOrg.role" class="gf-form-input width-10" ng-options="f for f in ['Viewer', 'Editor', 'Admin']"></select>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<button class="btn btn-success gf-form-btn" ng-click="addOrgUser()">Add</button>
|
<button class="btn btn-success gf-form-btn" ng-click="addOrgUser()">Add</button>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-select-wrapper">
|
<span class="gf-form-select-wrapper">
|
||||||
<select type="text" ng-model="org.role" class="gf-form-input max-width-12" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(org)">
|
<select type="text" ng-model="org.role" class="gf-form-input max-width-12" ng-options="f for f in ['Viewer', 'Editor', 'Admin']" ng-change="updateOrgUser(org)">
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="gf-form max-width-30">
|
<div class="gf-form max-width-30">
|
||||||
<span class="gf-form-label width-10">Role</span>
|
<span class="gf-form-label width-10">Role</span>
|
||||||
<select ng-model="ctrl.invite.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']">
|
<select ng-model="ctrl.invite.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Admin']">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<td>{{user.lastSeenAtAge}}</td>
|
<td>{{user.lastSeenAtAge}}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="gf-form-select-wrapper width-12">
|
<div class="gf-form-select-wrapper width-12">
|
||||||
<select type="text" ng-model="user.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="ctrl.updateOrgUser(user)">
|
<select type="text" ng-model="user.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Admin']" ng-change="ctrl.updateOrgUser(user)">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -120,14 +120,6 @@ export default class GraphiteQuery {
|
|||||||
this.segments.push({value: "select metric"});
|
this.segments.push({value: "select metric"});
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSelectMetric() {
|
|
||||||
if (this.segments.length > 0) {
|
|
||||||
return this.segments[this.segments.length - 1].value === 'select metric';
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addFunction(newFunc) {
|
addFunction(newFunc) {
|
||||||
this.functions.push(newFunc);
|
this.functions.push(newFunc);
|
||||||
this.moveAliasFuncLast();
|
this.moveAliasFuncLast();
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
|||||||
var oldTarget = this.queryModel.target.target;
|
var oldTarget = this.queryModel.target.target;
|
||||||
this.updateModelTarget();
|
this.updateModelTarget();
|
||||||
|
|
||||||
if (this.queryModel.target !== oldTarget && !this.queryModel.hasSelectMetric()) {
|
if (this.queryModel.target !== oldTarget) {
|
||||||
this.panelCtrl.refresh();
|
this.panelCtrl.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user