From 62ae80eeaec37f1fe2f4c87d426198129d4f3526 Mon Sep 17 00:00:00 2001 From: Liu Yubao Date: Thu, 17 Sep 2015 01:25:28 +0800 Subject: [PATCH 1/6] deduplicate tag value suggestions for OpenTSDB --- public/app/plugins/datasource/opentsdb/datasource.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js index e91fa4b8ac7..879daf06c73 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.js +++ b/public/app/plugins/datasource/opentsdb/datasource.js @@ -97,7 +97,9 @@ function (angular, _, kbn) { result = result.data.results; var tagvs = []; _.each(result, function(r) { - tagvs.push(r.tags[key]); + if (tagvs.indexOf(r.tags[key]) === -1) { + tagvs.push(r.tags[key]); + } }); return tagvs; }); From 4fccfbf54327e3450c5492056e521c9a8bdde134 Mon Sep 17 00:00:00 2001 From: Liu Yubao Date: Thu, 17 Sep 2015 01:36:36 +0800 Subject: [PATCH 2/6] increase limit to lookup unique metric tag values Default limit is 25 which is too small. Considering currently the /api/search/lookup query isn't narrowed down by selected tag keys and values (see https://github.com/grafana/grafana/pull/1433), the limit is set to 3000, this should be fine because people rarely create new graph panel. --- public/app/plugins/datasource/opentsdb/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js index 879daf06c73..612324e7543 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.js +++ b/public/app/plugins/datasource/opentsdb/datasource.js @@ -93,7 +93,7 @@ function (angular, _, kbn) { var m = metric + "{" + key + "=*}"; - return this._get('/api/search/lookup', {m: m}).then(function(result) { + return this._get('/api/search/lookup', {m: m, limit: 3000}).then(function(result) { result = result.data.results; var tagvs = []; _.each(result, function(r) { From 4c0262cbd00b91ba69ba8f3df3f634335ded3ef9 Mon Sep 17 00:00:00 2001 From: raj dutt Date: Wed, 16 Sep 2015 23:46:48 -0400 Subject: [PATCH 3/6] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aae9cbb30f2..e5b08e7a3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ it allows you to add queries of differnet data source types & instances to the s - See [Issue #2353](https://github.com/grafana/grafana/issues/2354) for more info. ** Other new Features && Enhancements** +- [Pull #2720](https://github.com/grafana/grafana/pull/2720). Admin: Initial basic quota support (per Org) - [Issue #2577](https://github.com/grafana/grafana/issues/2577). Panel: Resize handles in panel bottom right corners for easy width and height change - [Issue #2457](https://github.com/grafana/grafana/issues/2457). Admin: admin page for all grafana organizations (list / edit view) - [Issue #1186](https://github.com/grafana/grafana/issues/1186). Time Picker: New option `today`, will set time range from midnight to now From a7cc36f741d489f2c9142d632936a1a98d579e65 Mon Sep 17 00:00:00 2001 From: raj dutt Date: Wed, 16 Sep 2015 23:50:02 -0400 Subject: [PATCH 4/6] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b08e7a3a0..fca413673ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -185,6 +185,10 @@ Grunt & Watch tasks: # 2.0.0-Beta1 (2015-03-30) +**Important Note** + +Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated backend server. Please read the [Documentation](http://docs.grafana.org) for more detailed about this SIGNIFCANT change to Grafana + **New features** - [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site - [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site From 7dc2b364136a73dc66d46898eb83a9bfd73e821c Mon Sep 17 00:00:00 2001 From: Eugen Dinca Date: Thu, 17 Sep 2015 16:50:09 -0400 Subject: [PATCH 5/6] Added maxSeries option for groupByNode function --- public/app/plugins/datasource/graphite/gfunc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/graphite/gfunc.js b/public/app/plugins/datasource/graphite/gfunc.js index 6c637c21130..748bd25fb8a 100644 --- a/public/app/plugins/datasource/graphite/gfunc.js +++ b/public/app/plugins/datasource/graphite/gfunc.js @@ -242,7 +242,7 @@ function (_, $) { { name: "function", type: "string", - options: ['sum', 'avg'] + options: ['sum', 'avg', 'maxSeries'] } ], defaultParams: [3, "sum"] From 5e949b05646058ce1186a388db4f756ea5448521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 18 Sep 2015 08:36:58 +0200 Subject: [PATCH 6/6] fix(quota): fixed failing quota unit tests --- pkg/middleware/middleware.go | 94 ------------------------------- pkg/middleware/quota.go | 106 +++++++++++++++++++++++++++++++++++ pkg/middleware/quota_test.go | 8 ++- pkg/middleware/session.go | 4 ++ 4 files changed, 117 insertions(+), 95 deletions(-) create mode 100644 pkg/middleware/quota.go diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 8a61bdb3a0b..8704ec5a787 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -1,7 +1,6 @@ package middleware import ( - "fmt" "strconv" "strings" @@ -254,96 +253,3 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) { ctx.JSON(status, resp) } - -func Quota(target string) macaron.Handler { - return func(c *Context) { - limitReached, err := QuotaReached(c, target) - if err != nil { - c.JsonApiErr(500, "failed to get quota", err) - return - } - if limitReached { - c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil) - return - } - } -} - -func QuotaReached(c *Context, target string) (bool, error) { - if !setting.Quota.Enabled { - return false, nil - } - - // get the list of scopes that this target is valid for. Org, User, Global - scopes, err := m.GetQuotaScopes(target) - if err != nil { - return false, err - } - log.Info(fmt.Sprintf("checking quota for %s in scopes %v", target, scopes)) - - for _, scope := range scopes { - log.Info(fmt.Sprintf("checking scope %s", scope.Name)) - switch scope.Name { - case "global": - if scope.DefaultLimit < 0 { - continue - } - if scope.DefaultLimit == 0 { - return true, nil - } - if target == "session" { - usedSessions := sessionManager.Count() - if int64(usedSessions) > scope.DefaultLimit { - log.Info(fmt.Sprintf("%d sessions active, limit is %d", usedSessions, scope.DefaultLimit)) - return true, nil - } - continue - } - query := m.GetGlobalQuotaByTargetQuery{Target: scope.Target} - if err := bus.Dispatch(&query); err != nil { - return true, err - } - if query.Result.Used >= scope.DefaultLimit { - return true, nil - } - case "org": - if !c.IsSignedIn { - continue - } - query := m.GetOrgQuotaByTargetQuery{OrgId: c.OrgId, Target: scope.Target, Default: scope.DefaultLimit} - if err := bus.Dispatch(&query); err != nil { - return true, err - } - if query.Result.Limit < 0 { - continue - } - if query.Result.Limit == 0 { - return true, nil - } - - if query.Result.Used >= query.Result.Limit { - return true, nil - } - case "user": - if !c.IsSignedIn || c.UserId == 0 { - continue - } - query := m.GetUserQuotaByTargetQuery{UserId: c.UserId, Target: scope.Target, Default: scope.DefaultLimit} - if err := bus.Dispatch(&query); err != nil { - return true, err - } - if query.Result.Limit < 0 { - continue - } - if query.Result.Limit == 0 { - return true, nil - } - - if query.Result.Used >= query.Result.Limit { - return true, nil - } - } - } - - return false, nil -} diff --git a/pkg/middleware/quota.go b/pkg/middleware/quota.go new file mode 100644 index 00000000000..f6ba74d77df --- /dev/null +++ b/pkg/middleware/quota.go @@ -0,0 +1,106 @@ +package middleware + +import ( + "fmt" + + "github.com/Unknwon/macaron" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" +) + +func Quota(target string) macaron.Handler { + return func(c *Context) { + limitReached, err := QuotaReached(c, target) + if err != nil { + c.JsonApiErr(500, "failed to get quota", err) + return + } + if limitReached { + c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil) + return + } + } +} + +func QuotaReached(c *Context, target string) (bool, error) { + if !setting.Quota.Enabled { + return false, nil + } + + // get the list of scopes that this target is valid for. Org, User, Global + scopes, err := m.GetQuotaScopes(target) + if err != nil { + return false, err + } + + log.Debug(fmt.Sprintf("checking quota for %s in scopes %v", target, scopes)) + + for _, scope := range scopes { + log.Debug(fmt.Sprintf("checking scope %s", scope.Name)) + + switch scope.Name { + case "global": + if scope.DefaultLimit < 0 { + continue + } + if scope.DefaultLimit == 0 { + return true, nil + } + if target == "session" { + usedSessions := getSessionCount() + if int64(usedSessions) > scope.DefaultLimit { + log.Debug(fmt.Sprintf("%d sessions active, limit is %d", usedSessions, scope.DefaultLimit)) + return true, nil + } + continue + } + query := m.GetGlobalQuotaByTargetQuery{Target: scope.Target} + if err := bus.Dispatch(&query); err != nil { + return true, err + } + if query.Result.Used >= scope.DefaultLimit { + return true, nil + } + case "org": + if !c.IsSignedIn { + continue + } + query := m.GetOrgQuotaByTargetQuery{OrgId: c.OrgId, Target: scope.Target, Default: scope.DefaultLimit} + if err := bus.Dispatch(&query); err != nil { + return true, err + } + if query.Result.Limit < 0 { + continue + } + if query.Result.Limit == 0 { + return true, nil + } + + if query.Result.Used >= query.Result.Limit { + return true, nil + } + case "user": + if !c.IsSignedIn || c.UserId == 0 { + continue + } + query := m.GetUserQuotaByTargetQuery{UserId: c.UserId, Target: scope.Target, Default: scope.DefaultLimit} + if err := bus.Dispatch(&query); err != nil { + return true, err + } + if query.Result.Limit < 0 { + continue + } + if query.Result.Limit == 0 { + return true, nil + } + + if query.Result.Used >= query.Result.Limit { + return true, nil + } + } + } + + return false, nil +} diff --git a/pkg/middleware/quota_test.go b/pkg/middleware/quota_test.go index ae6b414b248..b68aa485fa7 100644 --- a/pkg/middleware/quota_test.go +++ b/pkg/middleware/quota_test.go @@ -1,16 +1,22 @@ package middleware import ( + "testing" + "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" - "testing" ) func TestMiddlewareQuota(t *testing.T) { Convey("Given the grafana quota middleware", t, func() { + getSessionCount = func() int { + return 4 + } + + setting.AnonymousEnabled = false setting.Quota = setting.QuotaSettings{ Enabled: true, Org: &setting.OrgQuota{ diff --git a/pkg/middleware/session.go b/pkg/middleware/session.go index 7b036b9790e..fc1512e5bd6 100644 --- a/pkg/middleware/session.go +++ b/pkg/middleware/session.go @@ -18,12 +18,16 @@ const ( var sessionManager *session.Manager var sessionOptions *session.Options var startSessionGC func() +var getSessionCount func() int func init() { startSessionGC = func() { sessionManager.GC() time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC) } + getSessionCount = func() int { + return sessionManager.Count() + } } func prepareOptions(opt *session.Options) *session.Options {