Przeglądaj źródła

Merge branch 'master' into v4.2.x

Torkel Ödegaard 8 lat temu
rodzic
commit
140a0982e8

+ 15 - 2
CHANGELOG.md

@@ -1,4 +1,17 @@
-# 4.2.0 (unreleased)
+# 4.3.0 (unreleased)
+
+## Minor Enchancements
+* **Threema**: Add emoji to Threema alert notifications [#7676](https://github.com/grafana/grafana/pull/7676) thx [@dbrgn](https://github.com/dbrgn)
+* **Panels**: Support dm3 unit [#7695](https://github.com/grafana/grafana/issues/7695) thx [@mitjaziv](https://github.com/mitjaziv)
+
+# 4.2.0-beta2 (unreleased)
+## Minor Enhancements
+* **Templates**: Prevent use of the prefix `__` for templates in web UI [#7678](https://github.com/grafana/grafana/issues/7678)
+
+## Bugfixes
+* **Webhook**: Use proxy settings from environment variables [#7710](https://github.com/grafana/grafana/issues/7710)
+
+# 4.2.0-beta1 (2017-02-27)
 
 ## Enhancements
 * **Telegram**: Added Telegram alert notifier [#7098](https://github.com/grafana/grafana/pull/7098), thx [@leonoff](https://github.com/leonoff)
@@ -13,7 +26,7 @@
 * **Alerting**: Uploading images for alert notifications is now optional [#7419](https://github.com/grafana/grafana/issues/7419)
 * **Dashboard**: Adds shortcut for collapsing/expanding all rows [#552](https://github.com/grafana/grafana/issues/552), thx [@mtanda](https://github.com/mtanda)
 * **Alerting**: Adds de duping of alert notifications [#7632](https://github.com/grafana/grafana/pull/7632)
-* **Orgs**: Sharing dashboards using Grafana share feature will not redirect to correct org. [#1613](https://github.com/grafana/grafana/issues/1613)
+* **Orgs**: Sharing dashboards using Grafana share feature will now redirect to correct org. [#1613](https://github.com/grafana/grafana/issues/1613)
 * **Pushover**: Add Pushover alert notifications [#7526](https://github.com/grafana/grafana/pull/7526) thx [@devkid](https://github.com/devkid)
 * **Threema**: Add Threema Gateway alert notification integration [#7482](https://github.com/grafana/grafana/pull/7482) thx [@dbrgn](https://github.com/dbrgn)
 

+ 1 - 1
appveyor.yml

@@ -30,7 +30,7 @@ install:
 build_script:
   - go run build.go build
   - grunt release
-  - go run build.go sha1-dist
+  - go run build.go sha-dist
   - cp dist/* .
 
 artifacts:

+ 9 - 9
build.go

@@ -5,7 +5,7 @@ package main
 import (
 	"bytes"
 	"crypto/md5"
-	"crypto/sha1"
+	"crypto/sha256"
 	"encoding/json"
 	"flag"
 	"fmt"
@@ -105,8 +105,8 @@ func main() {
 			grunt(gruntBuildArg("release")...)
 			createDebPackages()
 
-		case "sha1-dist":
-			sha1FilesInDist()
+		case "sha-dist":
+			shaFilesInDist()
 
 		case "latest":
 			makeLatestDistCopies()
@@ -522,14 +522,14 @@ func md5File(file string) error {
 	return out.Close()
 }
 
-func sha1FilesInDist() {
+func shaFilesInDist() {
 	filepath.Walk("./dist", func(path string, f os.FileInfo, err error) error {
 		if path == "./dist" {
 			return nil
 		}
 
-		if strings.Contains(path, ".sha1") == false {
-			err := sha1File(path)
+		if strings.Contains(path, ".sha256") == false {
+			err := shaFile(path)
 			if err != nil {
 				log.Printf("Failed to create sha file. error: %v\n", err)
 			}
@@ -538,20 +538,20 @@ func sha1FilesInDist() {
 	})
 }
 
-func sha1File(file string) error {
+func shaFile(file string) error {
 	fd, err := os.Open(file)
 	if err != nil {
 		return err
 	}
 	defer fd.Close()
 
-	h := sha1.New()
+	h := sha256.New()
 	_, err = io.Copy(h, fd)
 	if err != nil {
 		return err
 	}
 
-	out, err := os.Create(file + ".sha1")
+	out, err := os.Create(file + ".sha256")
 	if err != nil {
 		return err
 	}

+ 4 - 4
circle.yml

@@ -33,7 +33,7 @@ dependencies:
 
 test:
   override:
-     - bash scripts/circle-test.sh
+    - bash scripts/circle-test.sh
 
 deployment:
   gh_branch:
@@ -41,7 +41,7 @@ deployment:
     commands:
       - ./scripts/build/deploy.sh
       - ./scripts/build/sign_packages.sh
-      - go run build.go sha1-dist
+      - go run build.go sha-dist
       - aws s3 sync ./dist s3://$BUCKET_NAME/master
       - ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} master
       - ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
@@ -50,7 +50,7 @@ deployment:
     commands:
       - ./scripts/build/deploy.sh
       - ./scripts/build/sign_packages.sh
-      - go run build.go sha1-dist
+      - go run build.go sha-dist
       - aws s3 sync ./dist s3://$BUCKET_NAME/release
       - ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release
-
+      - ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} ${CIRCLE_TAG}

+ 5 - 0
docs/sources/alerting/notifications.md

@@ -101,4 +101,9 @@ config file.
 
 This is an optional requirement, you can get slack and email notifications without setting this up.
 
+# Configure the link back to Grafana from alert notifications
+
+All alert notifications contains a link back to the triggered alert in the Grafana instance. 
+This url is based on the [domain](/installation/configuration/#domain) setting in Grafana. 
+
 

+ 1 - 1
docs/sources/guides/whats-new-in-v4-1.md

@@ -7,7 +7,7 @@ type = "docs"
 name = "Version 4.1"
 identifier = "v4.1"
 parent = "whatsnew"
-weight = -1
+weight = 3
 +++
 
 

+ 88 - 0
docs/sources/guides/whats-new-in-v4-2.md

@@ -0,0 +1,88 @@
++++
+title = "What's New in Grafana v4.2"
+description = "Feature & improvement highlights for Grafana v4.2"
+keywords = ["grafana", "new", "documentation", "4.2.0"]
+type = "docs"
+[menu.docs]
+name = "Version 4.2"
+identifier = "v4.2"
+parent = "whatsnew"
+weight = -1
++++
+
+## Whats new in Grafana v4.2
+
+Grafana v4.2 Beta is now [available for download](/download/4_2_0/).
+Just like the last release this one contains lots bug fixes and minor improvements.
+We are very happy to say that 27 of 40 issues was closed by pull requests from the community.
+Big thumbs up!
+
+## Release Highlights
+
+- **Hipchat**: Adds support for sending alert notifications to hipchat [#6451](https://github.com/grafana/grafana/issues/6451), thx [@jregovic](https://github.com/jregovic)
+- **Telegram**: Added Telegram alert notifier [#7098](https://github.com/grafana/grafana/pull/7098), thx [@leonoff](https://github.com/leonoff)
+- **LINE**: Add LINE as alerting notification channel [#7301](https://github.com/grafana/grafana/pull/7301), thx [@huydx](https://github.com/huydx)
+- **Templating**: Make $__interval and $__interval_ms global built in variables that can be used in by any datasource (in panel queries), closes [#7190](https://github.com/grafana/grafana/issues/7190), closes [#6582](https://github.com/grafana/grafana/issues/6582)
+- **Alerting**: Adds deduping of alert notifications [#7632](https://github.com/grafana/grafana/pull/7632)
+- **Alerting**: Better information about why an alert triggered [#7035](https://github.com/grafana/grafana/issues/7035)
+- **Orgs**: Sharing dashboards using Grafana share feature will now redirect to correct org. [#6948](https://github.com/grafana/grafana/issues/6948)
+- [Full changelog](https://github.com/grafana/grafana/blob/master/CHANGELOG.md)
+
+### New alert notification channels
+
+This release adds **five** new alert notifications channels, all of them contributed by the community.
+
+* Hipchat
+* Telegram
+* LINE
+* Pushover
+* Threema
+
+### Templating
+
+We added two new global built in variables in grafana. `$__interval` and `$__interval_ms` are now reserved template names in grafana and can be used by any datasource.
+We might add more global built in variables in the future and if we do we will prefix them with `$__`. So please avoid using that in your template variables.
+
+### Dedupe alert notifications when running multiple servers
+
+In this release we will dedupe alert notificiations when you are running multiple servers.
+This makes it possible to run alerting on multiple servers and only get one notification.
+
+We currently solve this with sql transactions which puts some limitations for how many servers you can use to execute the same rules.
+3-5 servers should not be a problem but as always, it depends on how many alerts you have and how frequently they execute.
+
+Next up for a better HA situation is to add support for workload balancing between Grafana servers.
+
+### Alerting more info
+
+You can now see the reason why an alert triggered in the alert history. Its also easier to detect when an alert is set to `alerting` due to the `no_data` option.
+
+### Improved support for multi-org setup
+
+When loading dashboards we now set an query parameter called orgId. So we can detect from which org an user shared a dashboard.
+This makes it possible for users to share dashboards between orgs without changing org first.
+
+We aim to introduce [dashboard groups](https://github.com/grafana/grafana/issues/1611) sometime in the future which will introduce access control and user groups within one org.
+Making it possible to have users in multiple groups and have detailed access control.
+
+## Upgrade & Breaking changes
+
+If your using https in grafana we now force you to use tls 1.2 and the most secure ciphers.
+We think its better to be secure by default rather then making it configurable.
+If you want to run https with lower versions of tls we suggest you put a reserve proxy in front of grafana.
+
+If you have template variables name `$__interval` or `$__interval_ms` they will no longer work since these keywords
+are reserved as global built in variables. We might add more global built in variables in the future and if we do, we will prefix them with `$__`. So please avoid using that in your template variables.
+
+## Changelog
+
+Checkout the [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) file for a complete list
+of new features, changes, and bug fixes.
+
+## Download
+
+Head to [v4.2-beta download page](/download/4_2_0/) for download links & instructions.
+
+## Thanks
+
+A big thanks to all the Grafana users who contribute by submitting PRs, bug reports & feedback!

+ 17 - 2
docs/sources/installation/configuration.md

@@ -135,6 +135,10 @@ Path to the certificate file (if `protocol` is set to `https`).
 
 Path to the certificate key file (if `protocol` is set to `https`).
 
+### router_logging
+
+Set to true for Grafana to log all HTTP requests (not just errors). These are logged as Info level events
+to grafana log.
 <hr />
 
 <hr />
@@ -457,7 +461,7 @@ session provider you have configured.
 
 - **file:** session file path, e.g. `data/sessions`
 - **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
-- **postgres:** ex:  user=a password=b host=localhost port=5432 dbname=c sslmode=disable
+- **postgres:** ex:  user=a password=b host=localhost port=5432 dbname=c sslmode=require
 - **memcache:** ex:  127.0.0.1:11211
 - **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,prefix=grafana`
 
@@ -473,6 +477,17 @@ Mysql Example:
         PRIMARY KEY (`key`)
     ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
+Postgres Example:
+
+    CREATE TABLE session (
+        key       CHAR(16) NOT NULL,
+        data      BYTEA,
+        expiry    INTEGER NOT NULL,
+        PRIMARY KEY (key)
+    );
+
+Postgres valid `sslmode` are `disable`, `require` (default), `verify-ca`, and `verify-full`.
+
 ### cookie_name
 
 The name of the Grafana session cookie.
@@ -602,7 +617,7 @@ You can choose between (s3, webdav). If left empty Grafana will ignore the uploa
 ## [external_image_storage.s3]
 
 ### bucket_url
-Bucket URL for S3. AWS region can be specified within URL or defaults to 'us-east-1', e.g. 
+Bucket URL for S3. AWS region can be specified within URL or defaults to 'us-east-1', e.g.
 - http://grafana.s3.amazonaws.com/
 - https://grafana.s3-ap-southeast-2.amazonaws.com/
 - https://grafana.s3-cn-north-1.amazonaws.com.cn

+ 9 - 0
docs/sources/installation/debian.md

@@ -16,6 +16,7 @@ weight = 1
 Description | Download
 ------------ | -------------
 Stable for Debian-based Linux | [4.1.2 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_4.1.2-1486989747_amd64.deb)
+Beta for Debian-based Linux | [4.2.0-beta1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_4.2.0-beta1_amd64.deb)
 
 ## Install Stable
 
@@ -25,6 +26,14 @@ $ sudo apt-get install -y adduser libfontconfig
 $ sudo dpkg -i grafana_4.1.2-1486989747_amd64.deb
 ```
 
+## Install Beta
+
+```
+$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_4.2.0-beta1_amd64.deb
+$ sudo apt-get install -y adduser libfontconfig
+$ sudo dpkg -i grafana_4.2.0-beta1_amd64.deb
+```
+
 ## APT Repository
 
 Add the following line to your `/etc/apt/sources.list` file.

+ 1 - 0
docs/sources/installation/rpm.md

@@ -16,6 +16,7 @@ weight = 2
 Description | Download
 ------------ | -------------
 Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.1.2 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-4.1.2-1486989747.x86_64.rpm)
+Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.2.0-beta1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-4.2.0-beta1.x86_64.rpm)
 
 ## Install Stable
 

+ 1 - 0
docs/sources/installation/windows.md

@@ -14,6 +14,7 @@ weight = 3
 Description | Download
 ------------ | -------------
 Latest stable package for Windows | [grafana.4.1.2.windows-x64.zip](https://grafanarel.s3.amazonaws.com/builds/grafana-4.1.2.windows-x64.zip)
+Latest beta package for Windows | [grafana-4.2.0-beta1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/builds/grafana-4.2.0-beta1.windows-x64.zip)
 
 ## Configure
 

+ 49 - 9
pkg/api/cloudwatch/cloudwatch.go

@@ -114,7 +114,10 @@ func getCredentials(dsInfo *datasourceInfo) (*credentials.Credentials, error) {
 			DurationSeconds: aws.Int64(900),
 		}
 
-		stsSess := session.New()
+		stsSess, err := session.NewSession()
+		if err != nil {
+			return nil, err
+		}
 		stsCreds := credentials.NewChainCredentials(
 			[]credentials.Provider{
 				&credentials.EnvProvider{},
@@ -126,7 +129,11 @@ func getCredentials(dsInfo *datasourceInfo) (*credentials.Credentials, error) {
 			Credentials: stsCreds,
 		}
 
-		svc := sts.New(session.New(stsConfig), stsConfig)
+		sess, err := session.NewSession(stsConfig)
+		if err != nil {
+			return nil, err
+		}
+		svc := sts.New(sess, stsConfig)
 		resp, err := svc.AssumeRole(params)
 		if err != nil {
 			return nil, err
@@ -139,7 +146,10 @@ func getCredentials(dsInfo *datasourceInfo) (*credentials.Credentials, error) {
 		}
 	}
 
-	sess := session.New()
+	sess, err := session.NewSession()
+	if err != nil {
+		return nil, err
+	}
 	creds := credentials.NewChainCredentials(
 		[]credentials.Provider{
 			&credentials.StaticProvider{Value: credentials.Value{
@@ -185,7 +195,12 @@ func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {
@@ -232,7 +247,12 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {
@@ -273,7 +293,12 @@ func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {
@@ -316,7 +341,12 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {
@@ -360,7 +390,12 @@ func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {
@@ -396,7 +431,12 @@ func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
 		c.JsonApiErr(500, "Unable to call AWS API", err)
 		return
 	}
-	svc := ec2.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+	svc := ec2.New(sess, cfg)
 
 	reqParam := &struct {
 		Parameters struct {

+ 5 - 2
pkg/api/cloudwatch/metrics.go

@@ -258,8 +258,11 @@ func getAllMetrics(cwData *datasourceInfo) (cloudwatch.ListMetricsOutput, error)
 		Region:      aws.String(cwData.Region),
 		Credentials: creds,
 	}
-
-	svc := cloudwatch.New(session.New(cfg), cfg)
+	sess, err := session.NewSession(cfg)
+	if err != nil {
+		return cloudwatch.ListMetricsOutput{}, err
+	}
+	svc := cloudwatch.New(sess, cfg)
 
 	params := &cloudwatch.ListMetricsInput{
 		Namespace: aws.String(cwData.Namespace),

+ 9 - 2
pkg/components/imguploader/s3uploader.go

@@ -35,7 +35,10 @@ func NewS3Uploader(region, bucket, acl, accessKey, secretKey string) *S3Uploader
 }
 
 func (u *S3Uploader) Upload(imageDiskPath string) (string, error) {
-	sess := session.New()
+	sess, err := session.NewSession()
+	if err != nil {
+		return "", err
+	}
 	creds := credentials.NewChainCredentials(
 		[]credentials.Provider{
 			&credentials.StaticProvider{Value: credentials.Value{
@@ -58,7 +61,11 @@ func (u *S3Uploader) Upload(imageDiskPath string) (string, error) {
 		return "", err
 	}
 
-	svc := s3.New(session.New(cfg), cfg)
+	sess, err = session.NewSession(cfg)
+	if err != nil {
+		return "", err
+	}
+	svc := s3.New(sess, cfg)
 	params := &s3.PutObjectInput{
 		Bucket:      aws.String(u.bucket),
 		Key:         aws.String(key),

+ 7 - 1
pkg/middleware/session.go

@@ -1,6 +1,7 @@
 package middleware
 
 import (
+	"math/rand"
 	"time"
 
 	"github.com/go-macaron/session"
@@ -8,6 +9,7 @@ import (
 	_ "github.com/go-macaron/session/mysql"
 	_ "github.com/go-macaron/session/postgres"
 	_ "github.com/go-macaron/session/redis"
+	"github.com/grafana/grafana/pkg/log"
 	"gopkg.in/macaron.v1"
 )
 
@@ -22,10 +24,12 @@ var sessionManager *session.Manager
 var sessionOptions *session.Options
 var startSessionGC func()
 var getSessionCount func() int
+var sessionLogger = log.New("session")
 
 func init() {
 	startSessionGC = func() {
 		sessionManager.GC()
+		sessionLogger.Debug("Session GC")
 		time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC)
 	}
 	getSessionCount = func() int {
@@ -67,7 +71,9 @@ func Sessioner(options *session.Options) macaron.Handler {
 		panic(err)
 	}
 
-	go startSessionGC()
+	// start GC threads after some random seconds
+	rndSeconds := 10 + rand.Int63n(180)
+	time.AfterFunc(time.Duration(rndSeconds)*time.Second, startSessionGC)
 
 	return func(ctx *Context) {
 		ctx.Next()

+ 1 - 1
pkg/services/alerting/notifiers/email.go

@@ -16,7 +16,7 @@ func init() {
 	alerting.RegisterNotifier(&alerting.NotifierPlugin{
 		Type:        "email",
 		Name:        "Email",
-		Description: "Sends notifications using Grafana server configured STMP settings",
+		Description: "Sends notifications using Grafana server configured SMTP settings",
 		Factory:     NewEmailNotifier,
 		OptionsTemplate: `
       <h3 class="page-heading">Email addresses</h3>

+ 14 - 2
pkg/services/alerting/notifiers/threema.go

@@ -126,9 +126,21 @@ func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error
 	data.Set("to", notifier.RecipientID)
 	data.Set("secret", notifier.APISecret)
 
+	// Determine emoji
+	stateEmoji := ""
+	switch evalContext.Rule.State {
+	case m.AlertStateOK:
+		stateEmoji = "\u2705 " // White Heavy Check Mark
+	case m.AlertStateNoData:
+		stateEmoji = "\u2753 " // Black Question Mark Ornament
+	case m.AlertStateAlerting:
+		stateEmoji = "\u26A0 " // Warning sign
+	}
+
 	// Build message
-	message := fmt.Sprintf("%s\n\n*State:* %s\n*Message:* %s\n",
-		evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
+	message := fmt.Sprintf("%s%s\n\n*State:* %s\n*Message:* %s\n",
+		stateEmoji, evalContext.GetNotificationTitle(),
+		evalContext.Rule.Name, evalContext.Rule.Message)
 	ruleURL, err := evalContext.GetRuleUrl()
 	if err == nil {
 		message = message + fmt.Sprintf("*URL:* %s\n", ruleURL)

+ 123 - 0
pkg/services/sqlstore/logger.go

@@ -0,0 +1,123 @@
+package sqlstore
+
+import (
+	"fmt"
+
+	glog "github.com/grafana/grafana/pkg/log"
+
+	"github.com/go-xorm/core"
+)
+
+type XormLogger struct {
+	grafanaLog glog.Logger
+	level      glog.Lvl
+	showSQL    bool
+}
+
+func NewXormLogger(level glog.Lvl, grafanaLog glog.Logger) *XormLogger {
+	return &XormLogger{
+		grafanaLog: grafanaLog,
+		level:      level,
+		showSQL:    true,
+	}
+}
+
+// Error implement core.ILogger
+func (s *XormLogger) Err(v ...interface{}) error {
+	if s.level <= glog.LvlError {
+		s.grafanaLog.Error(fmt.Sprint(v...))
+	}
+	return nil
+}
+
+// Errorf implement core.ILogger
+func (s *XormLogger) Errf(format string, v ...interface{}) error {
+	if s.level <= glog.LvlError {
+		s.grafanaLog.Error(fmt.Sprintf(format, v...))
+	}
+	return nil
+}
+
+// Debug implement core.ILogger
+func (s *XormLogger) Debug(v ...interface{}) error {
+	if s.level <= glog.LvlDebug {
+		s.grafanaLog.Debug(fmt.Sprint(v...))
+	}
+	return nil
+}
+
+// Debugf implement core.ILogger
+func (s *XormLogger) Debugf(format string, v ...interface{}) error {
+	if s.level <= glog.LvlDebug {
+		s.grafanaLog.Debug(fmt.Sprintf(format, v...))
+	}
+	return nil
+}
+
+// Info implement core.ILogger
+func (s *XormLogger) Info(v ...interface{}) error {
+	if s.level <= glog.LvlInfo {
+		s.grafanaLog.Info(fmt.Sprint(v...))
+	}
+	return nil
+}
+
+// Infof implement core.ILogger
+func (s *XormLogger) Infof(format string, v ...interface{}) error {
+	if s.level <= glog.LvlInfo {
+		s.grafanaLog.Info(fmt.Sprintf(format, v...))
+	}
+	return nil
+}
+
+// Warn implement core.ILogger
+func (s *XormLogger) Warning(v ...interface{}) error {
+	if s.level <= glog.LvlWarn {
+		s.grafanaLog.Warn(fmt.Sprint(v...))
+	}
+	return nil
+}
+
+// Warnf implement core.ILogger
+func (s *XormLogger) Warningf(format string, v ...interface{}) error {
+	if s.level <= glog.LvlWarn {
+		s.grafanaLog.Warn(fmt.Sprintf(format, v...))
+	}
+	return nil
+}
+
+// Level implement core.ILogger
+func (s *XormLogger) Level() core.LogLevel {
+	switch s.level {
+	case glog.LvlError:
+		return core.LOG_ERR
+	case glog.LvlWarn:
+		return core.LOG_WARNING
+	case glog.LvlInfo:
+		return core.LOG_INFO
+	case glog.LvlDebug:
+		return core.LOG_DEBUG
+	default:
+		return core.LOG_ERR
+	}
+}
+
+// SetLevel implement core.ILogger
+func (s *XormLogger) SetLevel(l core.LogLevel) error {
+	return nil
+}
+
+// ShowSQL implement core.ILogger
+func (s *XormLogger) ShowSQL(show ...bool) {
+	s.grafanaLog.Error("ShowSQL", "show", "show")
+	if len(show) == 0 {
+		s.showSQL = true
+		return
+	}
+	s.showSQL = show[0]
+}
+
+// IsShowSQL implement core.ILogger
+func (s *XormLogger) IsShowSQL() bool {
+	return s.showSQL
+}

+ 3 - 0
pkg/services/sqlstore/sqlstore.go

@@ -160,6 +160,9 @@ func getEngine() (*xorm.Engine, error) {
 		engine.SetMaxConns(DbCfg.MaxConn)
 		engine.SetMaxOpenConns(DbCfg.MaxOpenConn)
 		engine.SetMaxIdleConns(DbCfg.MaxIdleConn)
+		// engine.SetLogger(NewXormLogger(log.LvlInfo, log.New("sqlstore.xorm")))
+		// engine.ShowSQL = true
+		// engine.ShowInfo = true
 	}
 	return engine, nil
 }

+ 4 - 0
pkg/tsdb/opentsdb/opentsdb.go

@@ -190,6 +190,10 @@ func (e *OpenTsdbExecutor) buildMetric(query *tsdb.Query) map[string]interface{}
 			rateOptions["resetValue"] = resetValue.MustFloat64()
 		}
 
+		if !counterMaxCheck && (!resetValueCheck || resetValue.MustFloat64() == 0) {
+			rateOptions["dropcounter"] = true
+		}
+
 		metric["rateOptions"] = rateOptions
 	}
 

+ 6 - 4
public/app/core/utils/kbn.js

@@ -488,6 +488,7 @@ function($, _) {
   kbn.valueFormats.litre  = kbn.formatBuilders.decimalSIPrefix('L');
   kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1);
   kbn.valueFormats.m3     = kbn.formatBuilders.decimalSIPrefix('m3');
+  kbn.valueFormats.dm3    = kbn.formatBuilders.decimalSIPrefix('dm3');
   kbn.valueFormats.gallons  = kbn.formatBuilders.fixedUnit('gal');
 
   // Flow
@@ -805,10 +806,11 @@ function($, _) {
       {
         text: 'volume',
         submenu: [
-          {text: 'millilitre',  value: 'mlitre' },
-          {text: 'litre',       value: 'litre'  },
-          {text: 'cubic metre', value: 'm3'     },
-          {text: 'gallons',     value: 'gallons'},
+          {text: 'millilitre',      value: 'mlitre' },
+          {text: 'litre',           value: 'litre'  },
+          {text: 'cubic metre',     value: 'm3'     },
+          {text: 'cubic decimetre', value: 'dm3'    },
+          {text: 'gallons',         value: 'gallons'},
         ]
       },
       {

+ 1 - 0
public/app/features/dashboard/dashnav/dashnav.ts

@@ -106,6 +106,7 @@ export class DashNavCtrl {
         confirmText: confirmText,
         yesText: 'Delete',
         onConfirm: function() {
+          $scope.dashboardMeta.canSave = false;
           $scope.deleteDashboardConfirmed();
         }
       });

+ 7 - 3
public/app/features/templating/datasource_variable.ts

@@ -2,7 +2,7 @@
 
 import _ from 'lodash';
 import kbn from 'app/core/utils/kbn';
-import {Variable, assignModelProperties, variableTypes} from './variable';
+import {Variable, containsVariable, assignModelProperties, variableTypes} from './variable';
 import {VariableSrv} from './variable_srv';
 
 export class DatasourceVariable implements Variable {
@@ -25,7 +25,7 @@ export class DatasourceVariable implements Variable {
   };
 
   /** @ngInject **/
-  constructor(private model, private datasourceSrv, private variableSrv) {
+  constructor(private model, private datasourceSrv, private variableSrv, private templateSrv) {
     assignModelProperties(this, model, this.defaults);
     this.refresh = 1;
   }
@@ -48,7 +48,8 @@ export class DatasourceVariable implements Variable {
     var regex;
 
     if (this.regex) {
-      regex = kbn.stringToJsRegex(this.regex);
+      regex = this.templateSrv.replace(this.regex, null, 'regex');
+      regex = kbn.stringToJsRegex(regex);
     }
 
     for (var i = 0; i < sources.length; i++) {
@@ -74,6 +75,9 @@ export class DatasourceVariable implements Variable {
   }
 
   dependsOn(variable) {
+    if (this.regex) {
+      return containsVariable(this.regex, variable.name);
+    }
     return false;
   }
 

+ 3 - 2
public/app/features/templating/interval_variable.ts

@@ -59,8 +59,9 @@ export class IntervalVariable implements Variable {
   }
 
   updateOptions() {
-   // extract options in comma separated string
-    this.options = _.map(this.query.split(/[,]+/), function(text) {
+    // extract options between quotes and/or comma
+    this.options = _.map(this.query.match(/(["'])(.*?)\1|\w+/g), function(text) {
+      text = text.replace(/["']+/g, '');
       return {text: text.trim(), value: text.trim()};
     });
 

+ 1 - 1
public/app/features/templating/partials/editor.html

@@ -124,7 +124,7 @@
               Step count <tip>How many times should the current time range be divided to calculate the value</tip>
             </span>
             <div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
-              <select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
+              <select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [1,2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
             </div>
           </div>
           <div class="gf-form">

+ 3 - 0
public/app/plugins/datasource/elasticsearch/metric_agg.js

@@ -162,6 +162,9 @@ function (angular, _, queryDef) {
     };
 
     $scope.getFieldsInternal = function() {
+      if ($scope.agg.type === 'cardinality') {
+        return $scope.getFields();
+      }
       return $scope.getFields({$fieldType: 'number'});
     };
 

+ 1 - 1
public/app/plugins/datasource/influxdb/influx_query.ts

@@ -167,7 +167,7 @@ export default class InfluxQuery {
     var policy = this.target.policy;
     var measurement = this.target.measurement || 'measurement';
 
-    if (!measurement.match('^/.*/')) {
+    if (!measurement.match('^/.*/$')) {
       measurement = '"' + measurement+ '"';
     } else if (interpolate) {
       measurement = this.templateSrv.replace(measurement, this.scopedVars, 'regex');

+ 2 - 0
public/app/plugins/panel/graph/legend.js

@@ -65,7 +65,9 @@ function (angular, _, $) {
           var el = $(e.currentTarget);
           var index = getSeriesIndexForElement(el);
           var seriesInfo = seriesList[index];
+          var scrollPosition = $($container.children('tbody')).scrollTop();
           ctrl.toggleSeries(seriesInfo, e);
+          $($container.children('tbody')).scrollTop(scrollPosition);
         }
 
         function sortLegend(e) {

+ 1 - 18
scripts/build/build.sh

@@ -7,20 +7,7 @@
 GOPATH=/go
 REPO_PATH=$GOPATH/src/github.com/grafana/grafana
 
-mkdir -p /go/src/github.com/grafana
-cd /go/src/github.com/grafana
-
-if [ "$CIRCLE_TAG" != "" ]; then
-  echo "Builing from tag $CIRCLE_TAG"
-  git clone https://github.com/grafana/grafana.git
-  cd $REPO_PATH
-  git checkout $CIRCLE_TAG
-else
-  echo "Building from branch $CIRCLE_BRANCH"
-  git clone --depth 1 https://github.com/grafana/grafana.git -b $CIRCLE_BRANCH
-  cd $REPO_PATH
-fi
-
+cd /go/src/github.com/grafana/grafana
 echo "current dir: $(pwd)"
 
 if [ "$CIRCLE_TAG" != "" ]; then
@@ -47,7 +34,3 @@ else
   echo "Packaging incremental build for $CIRCLE_BRANCH"
   go run build.go -buildNumber=${CIRCLE_BUILD_NUM} package latest
 fi
-
-cp dist/* /tmp/dist/
-
-

+ 3 - 1
scripts/build/deploy.sh

@@ -5,8 +5,10 @@ mkdir -p dist
 echo "Circle branch: ${CIRCLE_BRANCH}"
 echo "Circle tag: ${CIRCLE_TAG}"
 docker run -i -t --name gfbuild \
-  -v $(pwd)/dist:/tmp/dist \
+  -v $(pwd):/go/src/github.com/grafana/grafana \
   -e "CIRCLE_BRANCH=${CIRCLE_BRANCH}" \
   -e "CIRCLE_TAG=${CIRCLE_TAG}" \
   -e "CIRCLE_BUILD_NUM=${CIRCLE_BUILD_NUM}" \
   grafana/buildcontainer
+
+sudo chown -R ${USER:=$(/usr/bin/id -run)}:$USER dist

+ 3 - 2
scripts/circle-test.sh

@@ -24,7 +24,8 @@ exit_if_fail test -z "$(gofmt -s -l ./pkg | tee /dev/stderr)"
 echo "running go vet"
 exit_if_fail test -z "$(go vet ./pkg/... | tee /dev/stderr)"
 
+echo "building binaries"
 exit_if_fail go run build.go build
-exit_if_fail go test -v ./pkg/...
-
 
+echo "running go test"
+exit_if_fail go test -v ./pkg/...