Просмотр исходного кода

Merge branch 'master' into react-panels-step1

Torkel Ödegaard 7 лет назад
Родитель
Сommit
631af46c48

+ 0 - 1
.gitignore

@@ -69,7 +69,6 @@ debug.test
 /vendor/**/*.yml
 /vendor/**/*_test.go
 /vendor/**/.editorconfig
-/vendor/**/appengine*
 *.orig
 
 /devenv/bulk-dashboards/*.json

+ 3 - 1
CHANGELOG.md

@@ -8,6 +8,7 @@
 
 * **Datasource Proxy**: Keep trailing slash for datasource proxy requests [#13326](https://github.com/grafana/grafana/pull/13326), thx [@ryantxu](https://github.com/ryantxu)
 * **Elasticsearch**: Fix no limit size in terms aggregation for alerting queries [#13172](https://github.com/grafana/grafana/issues/13172), thx [@Yukinoshita-Yukino](https://github.com/Yukinoshita-Yukino)
+* **Units**: New clock time format, to format ms or second values as for example `01h:59m`, [#13635](https://github.com/grafana/grafana/issues/13635), thx [@franciscocpg](https://github.com/franciscocpg)
 
 ### Breaking changes
 
@@ -16,6 +17,7 @@
 # 5.3.1 (unreleased)
 
 * **Render**: Fix PhantomJS render of graph panel when legend displayed as table to the right [#13616](https://github.com/grafana/grafana/issues/13616)
+* **Stackdriver**: Filter option disappears after removing initial filter [#13607](https://github.com/grafana/grafana/issues/13607)
 
 # 5.3.0 (2018-10-10)
 
@@ -68,7 +70,7 @@
 * **Profile**: List teams that the user is member of in current/active organization [#12476](https://github.com/grafana/grafana/issues/12476)
 * **Configuration**: Allow auto-assigning users to specific organization (other than Main. Org) [#1823](https://github.com/grafana/grafana/issues/1823) [#12801](https://github.com/grafana/grafana/issues/12801), thx [@gzzo](https://github.com/gzzo) and [@ofosos](https://github.com/ofosos)
 * **Dataproxy**: Pass configured/auth headers to a Datasource [#10971](https://github.com/grafana/grafana/issues/10971), thx [@mrsiano](https://github.com/mrsiano)
-* ****: **: CloudWatch GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
+* **CloudWatch**: GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
 * **Postgres**: TimescaleDB support, e.g. use `time_bucket` for grouping by time when option enabled [#12680](https://github.com/grafana/grafana/pull/12680), thx [svenklemm](https://github.com/svenklemm)
 * **Cleanup**: Make temp file time to live configurable [#11607](https://github.com/grafana/grafana/issues/11607), thx [@xapon](https://github.com/xapon)
 

+ 2 - 3
docs/sources/plugins/developing/development.md

@@ -10,7 +10,7 @@ weight = 1
 
 # Developer Guide
 
-You can extend Grafana by writing your own plugins and then share then with other users in [our plugin repository](https://grafana.com/plugins).
+You can extend Grafana by writing your own plugins and then share them with other users in [our plugin repository](https://grafana.com/plugins).
 
 ## Short version
 
@@ -33,7 +33,7 @@ There are two blog posts about authoring a plugin that might also be of interest
 ## What languages?
 
 Since everything turns into javascript it's up to you to choose which language you want. That said it's probably a good idea to choose es6 or typescript since
-we use es6 classes in Grafana. So it's easier to get inspiration from the Grafana repo is you choose one of those languages.
+we use es6 classes in Grafana. So it's easier to get inspiration from the Grafana repo if you choose one of those languages.
 
 ## Buildscript
 
@@ -60,7 +60,6 @@ and [apps]({{< relref "apps.md" >}}) plugins in the documentation.
 The Grafana SDK is quite small so far and can be found here:
 
 - [SDK file in Grafana](https://github.com/grafana/grafana/blob/master/public/app/plugins/sdk.ts)
-- [SDK Readme](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md)
 
 The SDK contains three different plugin classes: PanelCtrl, MetricsPanelCtrl and QueryCtrl. For plugins of the panel type, the module.js file should export one of these. There are some extra classes for [data sources]({{< relref "datasources.md" >}}).
 

+ 1 - 1
package.json

@@ -80,7 +80,7 @@
     "style-loader": "^0.21.0",
     "systemjs": "0.20.19",
     "systemjs-plugin-css": "^0.1.36",
-    "ts-jest": "^23.1.4",
+    "ts-jest": "^23.10.4",
     "ts-loader": "^5.1.0",
     "tslib": "^1.9.3",
     "tslint": "^5.8.0",

+ 2 - 0
pkg/api/http_server.go

@@ -28,6 +28,7 @@ import (
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/registry"
+	"github.com/grafana/grafana/pkg/services/hooks"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/setting"
 )
@@ -52,6 +53,7 @@ type HTTPServer struct {
 	Bus           bus.Bus               `inject:""`
 	RenderService rendering.Service     `inject:""`
 	Cfg           *setting.Cfg          `inject:""`
+	HooksService  *hooks.HooksService   `inject:""`
 }
 
 func (hs *HTTPServer) Init() error {

+ 8 - 14
pkg/api/index.go

@@ -316,19 +316,6 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er
 		}
 
 		if c.IsGrafanaAdmin {
-			children := []*dtos.NavLink{
-				{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "gicon gicon-user"},
-				{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"},
-				{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "gicon gicon-preferences"},
-				{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"},
-			}
-
-			if setting.IsEnterprise {
-				children = append(children, &dtos.NavLink{Text: "Licensing", Id: "licensing", Url: setting.AppSubUrl + "/admin/licensing", Icon: "fa fa-fw fa-unlock-alt"})
-			}
-
-			children = append(children, &dtos.NavLink{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide", Icon: "fa fa-fw fa-eyedropper"})
-
 			cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
 				Text:         "Server Admin",
 				HideFromTabs: true,
@@ -336,7 +323,13 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er
 				Id:           "admin",
 				Icon:         "gicon gicon-shield",
 				Url:          setting.AppSubUrl + "/admin/users",
-				Children:     children,
+				Children: []*dtos.NavLink{
+					{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "gicon gicon-user"},
+					{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"},
+					{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "gicon gicon-preferences"},
+					{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"},
+					{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide", Icon: "fa fa-fw fa-eyedropper"},
+				},
 			})
 		}
 
@@ -357,6 +350,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er
 		},
 	})
 
+	hs.HooksService.RunIndexDataHooks(&data)
 	return &data, nil
 }
 

+ 7 - 1
pkg/services/alerting/notifiers/telegram.go

@@ -127,7 +127,13 @@ func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.Eval
 	var err error
 
 	imageFile, err = os.Open(evalContext.ImageOnDiskPath)
-	defer imageFile.Close()
+	defer func() {
+		err := imageFile.Close()
+		if err != nil {
+			log.Error2("Could not close Telegram inline image.", "err", err)
+		}
+	}()
+
 	if err != nil {
 		return nil, err
 	}

+ 30 - 0
pkg/services/hooks/hooks.go

@@ -0,0 +1,30 @@
+package hooks
+
+import (
+	"github.com/grafana/grafana/pkg/api/dtos"
+	"github.com/grafana/grafana/pkg/registry"
+)
+
+type IndexDataHook func(indexData *dtos.IndexViewData)
+
+type HooksService struct {
+	indexDataHooks []IndexDataHook
+}
+
+func init() {
+	registry.RegisterService(&HooksService{})
+}
+
+func (srv *HooksService) Init() error {
+	return nil
+}
+
+func (srv *HooksService) AddIndexDataHook(hook IndexDataHook) {
+	srv.indexDataHooks = append(srv.indexDataHooks, hook)
+}
+
+func (srv *HooksService) RunIndexDataHooks(indexData *dtos.IndexViewData) {
+	for _, hook := range srv.indexDataHooks {
+		hook(indexData)
+	}
+}

+ 0 - 2
pkg/services/sqlstore/user_auth_test.go

@@ -16,7 +16,6 @@ func TestUserAuth(t *testing.T) {
 	Convey("Given 5 users", t, func() {
 		var err error
 		var cmd *m.CreateUserCommand
-		users := []m.User{}
 		for i := 0; i < 5; i++ {
 			cmd = &m.CreateUserCommand{
 				Email: fmt.Sprint("user", i, "@test.com"),
@@ -25,7 +24,6 @@ func TestUserAuth(t *testing.T) {
 			}
 			err = CreateUser(context.Background(), cmd)
 			So(err, ShouldBeNil)
-			users = append(users, cmd.Result)
 		}
 
 		Reset(func() {

+ 1 - 1
pkg/tsdb/mssql/mssql_test.go

@@ -692,7 +692,7 @@ func TestMSSQL(t *testing.T) {
 					},
 				}
 
-				resp, err := endpoint.Query(nil, nil, query)
+				resp, err := endpoint.Query(context.Background(), nil, query)
 				So(err, ShouldBeNil)
 				queryResult := resp.Results["A"]
 				So(queryResult.Error, ShouldBeNil)

+ 1 - 1
pkg/tsdb/mysql/mysql_test.go

@@ -769,7 +769,7 @@ func TestMySQL(t *testing.T) {
 				},
 			}
 
-			resp, err := endpoint.Query(nil, nil, query)
+			resp, err := endpoint.Query(context.Background(), nil, query)
 			So(err, ShouldBeNil)
 			queryResult := resp.Results["A"]
 			So(queryResult.Error, ShouldBeNil)

+ 1 - 1
pkg/tsdb/postgres/postgres_test.go

@@ -701,7 +701,7 @@ func TestPostgres(t *testing.T) {
 					},
 				}
 
-				resp, err := endpoint.Query(nil, nil, query)
+				resp, err := endpoint.Query(context.Background(), nil, query)
 				So(err, ShouldBeNil)
 				queryResult := resp.Results["A"]
 				So(queryResult.Error, ShouldBeNil)

+ 71 - 0
public/app/core/specs/kbn.test.ts

@@ -399,6 +399,77 @@ describe('duration', () => {
   });
 });
 
+describe('clock', () => {
+  it('null', () => {
+    const str = kbn.toClock(null, 0);
+    expect(str).toBe('');
+  });
+  it('size less than 1 second', () => {
+    const str = kbn.toClock(999, 0);
+    expect(str).toBe('999ms');
+  });
+  describe('size less than 1 minute', () => {
+    it('default', () => {
+      const str = kbn.toClock(59999);
+      expect(str).toBe('59s:999ms');
+    });
+    it('decimals equals 0', () => {
+      const str = kbn.toClock(59999, 0);
+      expect(str).toBe('59s');
+    });
+  });
+  describe('size less than 1 hour', () => {
+    it('default', () => {
+      const str = kbn.toClock(3599999);
+      expect(str).toBe('59m:59s:999ms');
+    });
+    it('decimals equals 0', () => {
+      const str = kbn.toClock(3599999, 0);
+      expect(str).toBe('59m');
+    });
+    it('decimals equals 1', () => {
+      const str = kbn.toClock(3599999, 1);
+      expect(str).toBe('59m:59s');
+    });
+  });
+  describe('size greater than or equal 1 hour', () => {
+    it('default', () => {
+      const str = kbn.toClock(7199999);
+      expect(str).toBe('01h:59m:59s:999ms');
+    });
+    it('decimals equals 0', () => {
+      const str = kbn.toClock(7199999, 0);
+      expect(str).toBe('01h');
+    });
+    it('decimals equals 1', () => {
+      const str = kbn.toClock(7199999, 1);
+      expect(str).toBe('01h:59m');
+    });
+    it('decimals equals 2', () => {
+      const str = kbn.toClock(7199999, 2);
+      expect(str).toBe('01h:59m:59s');
+    });
+  });
+  describe('size greater than or equal 1 day', () => {
+    it('default', () => {
+      const str = kbn.toClock(89999999);
+      expect(str).toBe('24h:59m:59s:999ms');
+    });
+    it('decimals equals 0', () => {
+      const str = kbn.toClock(89999999, 0);
+      expect(str).toBe('24h');
+    });
+    it('decimals equals 1', () => {
+      const str = kbn.toClock(89999999, 1);
+      expect(str).toBe('24h:59m');
+    });
+    it('decimals equals 2', () => {
+      const str = kbn.toClock(89999999, 2);
+      expect(str).toBe('24h:59m:59s');
+    });
+  });
+});
+
 describe('volume', () => {
   it('1000m3', () => {
     const str = kbn.valueFormats['m3'](1000, 1, null);

+ 55 - 0
public/app/core/utils/kbn.ts

@@ -808,6 +808,51 @@ kbn.toDuration = (size, decimals, timeScale) => {
   return strings.join(', ');
 };
 
+kbn.toClock = (size, decimals) => {
+  if (size === null) {
+    return '';
+  }
+
+  // < 1 second
+  if (size < 1000) {
+    return moment.utc(size).format('SSS\\m\\s');
+  }
+
+  // < 1 minute
+  if (size < 60000) {
+    let format = 'ss\\s:SSS\\m\\s';
+    if (decimals === 0) {
+      format = 'ss\\s';
+    }
+    return moment.utc(size).format(format);
+  }
+
+  // < 1 hour
+  if (size < 3600000) {
+    let format = 'mm\\m:ss\\s:SSS\\m\\s';
+    if (decimals === 0) {
+      format = 'mm\\m';
+    } else if (decimals === 1) {
+      format = 'mm\\m:ss\\s';
+    }
+    return moment.utc(size).format(format);
+  }
+
+  let format = 'mm\\m:ss\\s:SSS\\m\\s';
+
+  const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`;
+
+  if (decimals === 0) {
+    format = '';
+  } else if (decimals === 1) {
+    format = 'mm\\m';
+  } else if (decimals === 2) {
+    format = 'mm\\m:ss\\s';
+  }
+
+  return format ? `${hours}:${moment.utc(size).format(format)}` : hours;
+};
+
 kbn.valueFormats.dtdurationms = (size, decimals) => {
   return kbn.toDuration(size, decimals, 'millisecond');
 };
@@ -824,6 +869,14 @@ kbn.valueFormats.timeticks = (size, decimals, scaledDecimals) => {
   return kbn.valueFormats.s(size / 100, decimals, scaledDecimals);
 };
 
+kbn.valueFormats.clockms = (size, decimals) => {
+  return kbn.toClock(size, decimals);
+};
+
+kbn.valueFormats.clocks = (size, decimals) => {
+  return kbn.toClock(size * 1000, decimals);
+};
+
 kbn.valueFormats.dateTimeAsIso = (epoch, isUtc) => {
   const time = isUtc ? moment.utc(epoch) : moment(epoch);
 
@@ -901,6 +954,8 @@ kbn.getUnitFormats = () => {
         { text: 'duration (s)', value: 'dtdurations' },
         { text: 'duration (hh:mm:ss)', value: 'dthms' },
         { text: 'Timeticks (s/100)', value: 'timeticks' },
+        { text: 'clock (ms)', value: 'clockms' },
+        { text: 'clock (s)', value: 'clocks' },
       ],
     },
     {

+ 1 - 1
public/app/plugins/datasource/prometheus/metric_find_query.ts

@@ -12,7 +12,7 @@ export default class PrometheusMetricFindQuery {
   }
 
   process() {
-    const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)\s*$/;
+    const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
     const metricNamesRegex = /^metrics\((.+)\)\s*$/;
     const queryResultRegex = /^query_result\((.+)\)\s*$/;
 

+ 1 - 1
public/app/plugins/datasource/stackdriver/filter_segments.ts

@@ -87,7 +87,7 @@ export class FilterSegments {
     }
 
     // remove condition if it is first segment
-    if (index === 0 && this.filterSegments[0].type === 'condition') {
+    if (index === 0 && this.filterSegments.length > 0 && this.filterSegments[0].type === 'condition') {
       this.filterSegments.splice(0, 1);
     }
   }

+ 3 - 1
public/app/plugins/panel/graph/graph.ts

@@ -714,7 +714,9 @@ class GraphElement {
     if (min && max && ticks) {
       const range = max - min;
       const secPerTick = range / ticks / 1000;
-      const oneDay = 86400000;
+      // Need have 10 milisecond margin on the day range
+      // As sometimes last 24 hour dashboard evaluates to more than 86400000
+      const oneDay = 86400010;
       const oneYear = 31536000000;
 
       if (secPerTick <= 45) {

Разница между файлами не показана из-за своего большого размера
+ 176 - 0
yarn.lock


Некоторые файлы не были показаны из-за большого количества измененных файлов