Commit 140a0982 by Torkel Ödegaard

Merge branch 'master' into v4.2.x

parents ee190d57 8e3f22d3
# 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)
......
......@@ -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:
......
......@@ -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
}
......
......@@ -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}
......@@ -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.
......@@ -7,7 +7,7 @@ type = "docs"
name = "Version 4.1"
identifier = "v4.1"
parent = "whatsnew"
weight = -1
weight = 3
+++
......
+++
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!
......@@ -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
......
......@@ -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.
......
......@@ -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
......
......@@ -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
......
......@@ -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 {
......
......@@ -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),
......
......@@ -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),
......
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()
......
......@@ -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>
......
......@@ -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)
......
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
}
......@@ -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
}
......
......@@ -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
}
......
......@@ -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'},
]
},
{
......
......@@ -106,6 +106,7 @@ export class DashNavCtrl {
confirmText: confirmText,
yesText: 'Delete',
onConfirm: function() {
$scope.dashboardMeta.canSave = false;
$scope.deleteDashboardConfirmed();
}
});
......
......@@ -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;
}
......
......@@ -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()};
});
......
......@@ -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">
......
......@@ -162,6 +162,9 @@ function (angular, _, queryDef) {
};
$scope.getFieldsInternal = function() {
if ($scope.agg.type === 'cardinality') {
return $scope.getFields();
}
return $scope.getFields({$fieldType: 'number'});
};
......
......@@ -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');
......
......@@ -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) {
......
......@@ -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/
......@@ -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
......@@ -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/...
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment