diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c540f02035..b078bb892ce 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/appveyor.yml b/appveyor.yml index 25e4181ed80..303c3abca9e 100644 --- a/appveyor.yml +++ b/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: diff --git a/build.go b/build.go index 5c04b78cecf..7943bd8716c 100644 --- a/build.go +++ b/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 } diff --git a/circle.yml b/circle.yml index faa50cb5c81..9c5f0f1ccbd 100644 --- a/circle.yml +++ b/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} diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index 9f8d3c36120..d0dd19cbcdc 100644 --- a/docs/sources/alerting/notifications.md +++ b/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. + diff --git a/docs/sources/guides/whats-new-in-v4-1.md b/docs/sources/guides/whats-new-in-v4-1.md index 59e6236af40..bd2b0f1b75f 100644 --- a/docs/sources/guides/whats-new-in-v4-1.md +++ b/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 +++ diff --git a/docs/sources/guides/whats-new-in-v4-2.md b/docs/sources/guides/whats-new-in-v4-2.md new file mode 100644 index 00000000000..44aa3a45dc0 --- /dev/null +++ b/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! diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 4a2b60cb48d..ba44dd30658 100644 --- a/docs/sources/installation/configuration.md +++ b/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.

@@ -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 diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index fb5c649ad1d..607d683bbb2 100644 --- a/docs/sources/installation/debian.md +++ b/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. diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index 56125a4951a..d329b7032c4 100644 --- a/docs/sources/installation/rpm.md +++ b/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 diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index 73816d98a40..b495ad81e98 100644 --- a/docs/sources/installation/windows.md +++ b/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 diff --git a/pkg/api/cloudwatch/cloudwatch.go b/pkg/api/cloudwatch/cloudwatch.go index 1b796be874e..1cf4b671e2d 100644 --- a/pkg/api/cloudwatch/cloudwatch.go +++ b/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 { diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 6b953bdf0fc..b41bb20d6c8 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/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), diff --git a/pkg/components/imguploader/s3uploader.go b/pkg/components/imguploader/s3uploader.go index 5f476b9e366..4f8632f965c 100644 --- a/pkg/components/imguploader/s3uploader.go +++ b/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), diff --git a/pkg/middleware/session.go b/pkg/middleware/session.go index 0700612ecc2..dad2ad20efc 100644 --- a/pkg/middleware/session.go +++ b/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() diff --git a/pkg/services/alerting/notifiers/email.go b/pkg/services/alerting/notifiers/email.go index 4058d3860b5..dcf71f0f99e 100644 --- a/pkg/services/alerting/notifiers/email.go +++ b/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: `

Email addresses

diff --git a/pkg/services/alerting/notifiers/threema.go b/pkg/services/alerting/notifiers/threema.go index a710d686d28..80c84c26a24 100644 --- a/pkg/services/alerting/notifiers/threema.go +++ b/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) diff --git a/pkg/services/sqlstore/logger.go b/pkg/services/sqlstore/logger.go new file mode 100644 index 00000000000..ae1145c21b0 --- /dev/null +++ b/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 +} diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 0d16d57bcde..ef22dd9b6c6 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/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 } diff --git a/pkg/tsdb/opentsdb/opentsdb.go b/pkg/tsdb/opentsdb/opentsdb.go index 684f48bfc59..c0ba6603b20 100644 --- a/pkg/tsdb/opentsdb/opentsdb.go +++ b/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 } diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index 819206bf94a..f5255b52697 100644 --- a/public/app/core/utils/kbn.js +++ b/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'}, ] }, { diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 33bb4411640..faaac986ee7 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/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(); } }); diff --git a/public/app/features/templating/datasource_variable.ts b/public/app/features/templating/datasource_variable.ts index bfd4d965029..41f2262ab4a 100644 --- a/public/app/features/templating/datasource_variable.ts +++ b/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; } diff --git a/public/app/features/templating/interval_variable.ts b/public/app/features/templating/interval_variable.ts index ab1b0e59442..10977596057 100644 --- a/public/app/features/templating/interval_variable.ts +++ b/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()}; }); diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index 5f844931b4c..2d514060cb7 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -124,7 +124,7 @@ Step count How many times should the current time range be divided to calculate the value
- +
diff --git a/public/app/plugins/datasource/elasticsearch/metric_agg.js b/public/app/plugins/datasource/elasticsearch/metric_agg.js index 9971c084882..c0c4ffc08d4 100644 --- a/public/app/plugins/datasource/elasticsearch/metric_agg.js +++ b/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'}); }; diff --git a/public/app/plugins/datasource/influxdb/influx_query.ts b/public/app/plugins/datasource/influxdb/influx_query.ts index 065b85cf175..074931b5d23 100644 --- a/public/app/plugins/datasource/influxdb/influx_query.ts +++ b/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'); diff --git a/public/app/plugins/panel/graph/legend.js b/public/app/plugins/panel/graph/legend.js index fd5abfb2db2..2cdb1821793 100644 --- a/public/app/plugins/panel/graph/legend.js +++ b/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) { diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 6e1b2cbe5b9..4f16ee240c4 100755 --- a/scripts/build/build.sh +++ b/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/ - - diff --git a/scripts/build/deploy.sh b/scripts/build/deploy.sh index bfc735e4c79..49b2a9e3a7c 100755 --- a/scripts/build/deploy.sh +++ b/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 diff --git a/scripts/circle-test.sh b/scripts/circle-test.sh index e11007d8e76..a3a2790bcdc 100755 --- a/scripts/circle-test.sh +++ b/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 + +echo "running go test" exit_if_fail go test -v ./pkg/... - -