Merge branch 'master' into getting-started-panel

This commit is contained in:
Torkel Ödegaard
2016-11-24 13:17:22 +01:00
1047 changed files with 32752 additions and 766238 deletions
+4
View File
@@ -103,6 +103,10 @@ func GetAlerts(c *middleware.Context) Response {
// POST /api/alerts/test
func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
backendCmd := alerting.AlertTestCommand{
OrgId: c.OrgId,
Dashboard: dto.Dashboard,
+10
View File
@@ -131,6 +131,16 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
}
}
validateAlertsCmd := alerting.ValidateDashboardAlertsCommand{
OrgId: c.OrgId,
UserId: c.UserId,
Dashboard: dash,
}
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
return ApiError(500, "Invalid alert data. Cannot save dashboard", err)
}
err := bus.Dispatch(&cmd)
if err != nil {
if err == m.ErrDashboardWithSameNameExists {
+3
View File
@@ -38,6 +38,9 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response {
cmd.UserId = userToAdd.Id
if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrOrgUserAlreadyAdded {
return ApiError(409, "User is already member of this organization", nil)
}
return ApiError(500, "Could not add user to organization", err)
}
+6 -1
View File
@@ -35,7 +35,12 @@ func RenderToPng(params *RenderOpts) (string, error) {
executable = executable + ".exe"
}
url := fmt.Sprintf("%s://localhost:%s/%s", setting.Protocol, setting.HttpPort, params.Path)
localAddress := "localhost"
if setting.HttpAddr != setting.DEFAULT_HTTP_ADDR {
localAddress = setting.HttpAddr
}
url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localAddress, setting.HttpPort, params.Path)
binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable))
scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
+17
View File
@@ -11,8 +11,25 @@ type UpdateDashboardAlertsCommand struct {
Dashboard *m.Dashboard
}
type ValidateDashboardAlertsCommand struct {
UserId int64
OrgId int64
Dashboard *m.Dashboard
}
func init() {
bus.AddHandler("alerting", updateDashboardAlerts)
bus.AddHandler("alerting", validateDashboardAlerts)
}
func validateDashboardAlerts(cmd *ValidateDashboardAlertsCommand) error {
extractor := NewDashAlertExtractor(cmd.Dashboard, cmd.OrgId)
if _, err := extractor.GetAlerts(); err != nil {
return err
}
return nil
}
func updateDashboardAlerts(cmd *UpdateDashboardAlertsCommand) error {
@@ -3,6 +3,8 @@ package conditions
import (
"math"
"sort"
"github.com/grafana/grafana/pkg/tsdb"
"gopkg.in/guregu/null.v3"
)
@@ -71,6 +73,24 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
break
}
}
case "median":
var values []float64
for _, v := range series.Points {
if v[0].Valid {
allNull = false
values = append(values, v[0].Float64)
}
}
if len(values) >= 1 {
sort.Float64s(values)
length := len(values)
if length%2 == 1 {
value = values[(length-1)/2]
} else {
value = (values[(length/2)-1] + values[length/2]) / 2
}
}
}
if allNull {
@@ -41,6 +41,20 @@ func TestSimpleReducer(t *testing.T) {
So(result, ShouldEqual, float64(3000))
})
Convey("median odd amount of numbers", func() {
result := testReducer("median", 1, 2, 3000)
So(result, ShouldEqual, float64(2))
})
Convey("median even amount of numbers", func() {
result := testReducer("median", 1, 2, 4, 3000)
So(result, ShouldEqual, float64(3))
})
Convey("median with one values", func() {
result := testReducer("median", 1)
So(result, ShouldEqual, float64(1))
})
})
}
+4
View File
@@ -23,6 +23,7 @@ func NewEvalHandler() *DefaultEvalHandler {
func (e *DefaultEvalHandler) Eval(context *EvalContext) {
firing := true
noDataFound := true
conditionEvals := ""
for i := 0; i < len(context.Rule.Conditions); i++ {
@@ -40,8 +41,10 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) {
// calculating Firing based on operator
if cr.Operator == "or" {
firing = firing || cr.Firing
noDataFound = noDataFound || cr.NoDataFound
} else {
firing = firing && cr.Firing
noDataFound = noDataFound && cr.NoDataFound
}
if i > 0 {
@@ -55,6 +58,7 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) {
context.ConditionEvals = conditionEvals + " = " + strconv.FormatBool(firing)
context.Firing = firing
context.NoDataFound = noDataFound
context.EndTime = time.Now()
elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond
metrics.M_Alerting_Exeuction_Time.Update(elapsedTime)
+38 -1
View File
@@ -11,10 +11,11 @@ type conditionStub struct {
firing bool
operator string
matches []*EvalMatch
noData bool
}
func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) {
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator}, nil
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil
}
func TestAlertingExecutor(t *testing.T) {
@@ -127,5 +128,41 @@ func TestAlertingExecutor(t *testing.T) {
So(context.Firing, ShouldEqual, true)
So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true")
})
Convey("Should return no data if one condition has nodata", func() {
context := NewEvalContext(context.TODO(), &Rule{
Conditions: []Condition{
&conditionStub{operator: "and", noData: true},
},
})
handler.Eval(context)
So(context.Firing, ShouldEqual, false)
So(context.NoDataFound, ShouldBeTrue)
})
Convey("Should return no data if both conditions have no data and using AND", func() {
context := NewEvalContext(context.TODO(), &Rule{
Conditions: []Condition{
&conditionStub{operator: "and", noData: true},
&conditionStub{operator: "and", noData: false},
},
})
handler.Eval(context)
So(context.NoDataFound, ShouldBeFalse)
})
Convey("Should not return no data if both conditions have no data and using OR", func() {
context := NewEvalContext(context.TODO(), &Rule{
Conditions: []Condition{
&conditionStub{operator: "or", noData: true},
&conditionStub{operator: "or", noData: false},
},
})
handler.Eval(context)
So(context.NoDataFound, ShouldBeTrue)
})
})
}
+2 -4
View File
@@ -55,10 +55,8 @@ func (n *RootNotifier) Notify(context *EvalContext) error {
return nil
}
err = n.uploadImage(context)
if err != nil {
n.log.Error("Failed to upload alert panel image", "error", err)
return err
if err = n.uploadImage(context); err != nil {
n.log.Error("Failed to upload alert panel image.", "error", err)
}
return n.sendNotifications(context, notifiers)
+4 -3
View File
@@ -24,8 +24,9 @@ import (
type Scheme string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
HTTP Scheme = "http"
HTTPS Scheme = "https"
DEFAULT_HTTP_ADDR string = "0.0.0.0"
)
const (
@@ -474,7 +475,7 @@ func NewConfigContext(args *CommandLineArgs) error {
}
Domain = server.Key("domain").MustString("localhost")
HttpAddr = server.Key("http_addr").MustString("0.0.0.0")
HttpAddr = server.Key("http_addr").MustString(DEFAULT_HTTP_ADDR)
HttpPort = server.Key("http_port").MustString("3000")
RouterLogging = server.Key("router_logging").MustBool(false)
EnableGzip = server.Key("enable_gzip").MustBool(false)