Commit 60ef7d87 by Torkel Ödegaard

Merge branch 'master' into create-annotations

parents a151de1d aa47b9bf
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
## Minor Enchancements ## Minor Enchancements
* **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778) * **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778)
* **Prometheus**: Step parameter changed semantics to min step to reduce the load on Prometheus and rendering in browser [#8073](https://github.com/grafana/grafana/pull/8073), thx [@bobrik](https://github.com/bobrik)
* **Templating**: Should not be possible to create self-referencing (recursive) template variable definitions [#7614](https://github.com/grafana/grafana/issues/7614) thx [@thuck](https://github.com/thuck) * **Templating**: Should not be possible to create self-referencing (recursive) template variable definitions [#7614](https://github.com/grafana/grafana/issues/7614) thx [@thuck](https://github.com/thuck)
* **Cloudwatch**: Correctly obtain IAM roles within ECS container tasks [#7892](https://github.com/grafana/grafana/issues/7892) thx [@gomlgs](https://github.com/gomlgs) * **Cloudwatch**: Correctly obtain IAM roles within ECS container tasks [#7892](https://github.com/grafana/grafana/issues/7892) thx [@gomlgs](https://github.com/gomlgs)
* **Units**: New number format: Scientific notation [#7781](https://github.com/grafana/grafana/issues/7781) thx [@cadnce](https://github.com/cadnce) * **Units**: New number format: Scientific notation [#7781](https://github.com/grafana/grafana/issues/7781) thx [@cadnce](https://github.com/cadnce)
......
...@@ -34,7 +34,7 @@ func New(logger string, ctx ...interface{}) Logger { ...@@ -34,7 +34,7 @@ func New(logger string, ctx ...interface{}) Logger {
func Trace(format string, v ...interface{}) { func Trace(format string, v ...interface{}) {
var message string var message string
if len(v) > 0 { if len(v) > 0 {
message = fmt.Sprintf(format, v) message = fmt.Sprintf(format, v...)
} else { } else {
message = format message = format
} }
...@@ -45,7 +45,7 @@ func Trace(format string, v ...interface{}) { ...@@ -45,7 +45,7 @@ func Trace(format string, v ...interface{}) {
func Debug(format string, v ...interface{}) { func Debug(format string, v ...interface{}) {
var message string var message string
if len(v) > 0 { if len(v) > 0 {
message = fmt.Sprintf(format, v) message = fmt.Sprintf(format, v...)
} else { } else {
message = format message = format
} }
...@@ -60,7 +60,7 @@ func Debug2(message string, v ...interface{}) { ...@@ -60,7 +60,7 @@ func Debug2(message string, v ...interface{}) {
func Info(format string, v ...interface{}) { func Info(format string, v ...interface{}) {
var message string var message string
if len(v) > 0 { if len(v) > 0 {
message = fmt.Sprintf(format, v) message = fmt.Sprintf(format, v...)
} else { } else {
message = format message = format
} }
...@@ -75,7 +75,7 @@ func Info2(message string, v ...interface{}) { ...@@ -75,7 +75,7 @@ func Info2(message string, v ...interface{}) {
func Warn(format string, v ...interface{}) { func Warn(format string, v ...interface{}) {
var message string var message string
if len(v) > 0 { if len(v) > 0 {
message = fmt.Sprintf(format, v) message = fmt.Sprintf(format, v...)
} else { } else {
message = format message = format
} }
...@@ -88,7 +88,7 @@ func Warn2(message string, v ...interface{}) { ...@@ -88,7 +88,7 @@ func Warn2(message string, v ...interface{}) {
} }
func Error(skip int, format string, v ...interface{}) { func Error(skip int, format string, v ...interface{}) {
Root.Error(fmt.Sprintf(format, v)) Root.Error(fmt.Sprintf(format, v...))
} }
func Error2(message string, v ...interface{}) { func Error2(message string, v ...interface{}) {
...@@ -96,7 +96,7 @@ func Error2(message string, v ...interface{}) { ...@@ -96,7 +96,7 @@ func Error2(message string, v ...interface{}) {
} }
func Critical(skip int, format string, v ...interface{}) { func Critical(skip int, format string, v ...interface{}) {
Root.Crit(fmt.Sprintf(format, v)) Root.Crit(fmt.Sprintf(format, v...))
} }
func Fatal(skip int, format string, v ...interface{}) { func Fatal(skip int, format string, v ...interface{}) {
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
func initContextWithAuthProxy(ctx *Context) bool { func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
if !setting.AuthProxyEnabled { if !setting.AuthProxyEnabled {
return false return false
} }
...@@ -30,6 +30,7 @@ func initContextWithAuthProxy(ctx *Context) bool { ...@@ -30,6 +30,7 @@ func initContextWithAuthProxy(ctx *Context) bool {
} }
query := getSignedInUserQueryForProxyAuth(proxyHeaderValue) query := getSignedInUserQueryForProxyAuth(proxyHeaderValue)
query.OrgId = orgId
if err := bus.Dispatch(query); err != nil { if err := bus.Dispatch(query); err != nil {
if err != m.ErrUserNotFound { if err != m.ErrUserNotFound {
ctx.Handle(500, "Failed to find user specified in auth proxy header", err) ctx.Handle(500, "Failed to find user specified in auth proxy header", err)
...@@ -46,7 +47,7 @@ func initContextWithAuthProxy(ctx *Context) bool { ...@@ -46,7 +47,7 @@ func initContextWithAuthProxy(ctx *Context) bool {
ctx.Handle(500, "Failed to create user specified in auth proxy header", err) ctx.Handle(500, "Failed to create user specified in auth proxy header", err)
return true return true
} }
query = &m.GetSignedInUserQuery{UserId: cmd.Result.Id} query = &m.GetSignedInUserQuery{UserId: cmd.Result.Id, OrgId: orgId}
if err := bus.Dispatch(query); err != nil { if err := bus.Dispatch(query); err != nil {
ctx.Handle(500, "Failed find user after creation", err) ctx.Handle(500, "Failed find user after creation", err)
return true return true
......
...@@ -39,6 +39,12 @@ func GetContextHandler() macaron.Handler { ...@@ -39,6 +39,12 @@ func GetContextHandler() macaron.Handler {
Logger: log.New("context"), Logger: log.New("context"),
} }
orgId := int64(0)
orgIdHeader := ctx.Req.Header.Get("X-Grafana-Org-Id")
if orgIdHeader != "" {
orgId, _ = strconv.ParseInt(orgIdHeader, 10, 64)
}
// the order in which these are tested are important // the order in which these are tested are important
// look for api key in Authorization header first // look for api key in Authorization header first
// then init session and look for userId in session // then init session and look for userId in session
...@@ -46,9 +52,9 @@ func GetContextHandler() macaron.Handler { ...@@ -46,9 +52,9 @@ func GetContextHandler() macaron.Handler {
// then test if anonymous access is enabled // then test if anonymous access is enabled
if initContextWithRenderAuth(ctx) || if initContextWithRenderAuth(ctx) ||
initContextWithApiKey(ctx) || initContextWithApiKey(ctx) ||
initContextWithBasicAuth(ctx) || initContextWithBasicAuth(ctx, orgId) ||
initContextWithAuthProxy(ctx) || initContextWithAuthProxy(ctx, orgId) ||
initContextWithUserSessionCookie(ctx) || initContextWithUserSessionCookie(ctx, orgId) ||
initContextWithAnonymousUser(ctx) { initContextWithAnonymousUser(ctx) {
} }
...@@ -68,18 +74,18 @@ func initContextWithAnonymousUser(ctx *Context) bool { ...@@ -68,18 +74,18 @@ func initContextWithAnonymousUser(ctx *Context) bool {
if err := bus.Dispatch(&orgQuery); err != nil { if err := bus.Dispatch(&orgQuery); err != nil {
log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err) log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err)
return false return false
} else {
ctx.IsSignedIn = false
ctx.AllowAnonymous = true
ctx.SignedInUser = &m.SignedInUser{}
ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
ctx.OrgId = orgQuery.Result.Id
ctx.OrgName = orgQuery.Result.Name
return true
} }
ctx.IsSignedIn = false
ctx.AllowAnonymous = true
ctx.SignedInUser = &m.SignedInUser{}
ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
ctx.OrgId = orgQuery.Result.Id
ctx.OrgName = orgQuery.Result.Name
return true
} }
func initContextWithUserSessionCookie(ctx *Context) bool { func initContextWithUserSessionCookie(ctx *Context, orgId int64) bool {
// initialize session // initialize session
if err := ctx.Session.Start(ctx); err != nil { if err := ctx.Session.Start(ctx); err != nil {
ctx.Logger.Error("Failed to start session", "error", err) ctx.Logger.Error("Failed to start session", "error", err)
...@@ -91,15 +97,15 @@ func initContextWithUserSessionCookie(ctx *Context) bool { ...@@ -91,15 +97,15 @@ func initContextWithUserSessionCookie(ctx *Context) bool {
return false return false
} }
query := m.GetSignedInUserQuery{UserId: userId} query := m.GetSignedInUserQuery{UserId: userId, OrgId: orgId}
if err := bus.Dispatch(&query); err != nil { if err := bus.Dispatch(&query); err != nil {
ctx.Logger.Error("Failed to get user with id", "userId", userId) ctx.Logger.Error("Failed to get user with id", "userId", userId)
return false return false
} else {
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
return true
} }
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
return true
} }
func initContextWithApiKey(ctx *Context) bool { func initContextWithApiKey(ctx *Context) bool {
...@@ -114,30 +120,31 @@ func initContextWithApiKey(ctx *Context) bool { ...@@ -114,30 +120,31 @@ func initContextWithApiKey(ctx *Context) bool {
ctx.JsonApiErr(401, "Invalid API key", err) ctx.JsonApiErr(401, "Invalid API key", err)
return true return true
} }
// fetch key // fetch key
keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId} keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
if err := bus.Dispatch(&keyQuery); err != nil { if err := bus.Dispatch(&keyQuery); err != nil {
ctx.JsonApiErr(401, "Invalid API key", err) ctx.JsonApiErr(401, "Invalid API key", err)
return true return true
} else { }
apikey := keyQuery.Result
// validate api key apikey := keyQuery.Result
if !apikeygen.IsValid(decoded, apikey.Key) {
ctx.JsonApiErr(401, "Invalid API key", err)
return true
}
ctx.IsSignedIn = true // validate api key
ctx.SignedInUser = &m.SignedInUser{} if !apikeygen.IsValid(decoded, apikey.Key) {
ctx.OrgRole = apikey.Role ctx.JsonApiErr(401, "Invalid API key", err)
ctx.ApiKeyId = apikey.Id
ctx.OrgId = apikey.OrgId
return true return true
} }
ctx.IsSignedIn = true
ctx.SignedInUser = &m.SignedInUser{}
ctx.OrgRole = apikey.Role
ctx.ApiKeyId = apikey.Id
ctx.OrgId = apikey.OrgId
return true
} }
func initContextWithBasicAuth(ctx *Context) bool { func initContextWithBasicAuth(ctx *Context, orgId int64) bool {
if !setting.BasicAuthEnabled { if !setting.BasicAuthEnabled {
return false return false
...@@ -168,15 +175,15 @@ func initContextWithBasicAuth(ctx *Context) bool { ...@@ -168,15 +175,15 @@ func initContextWithBasicAuth(ctx *Context) bool {
return true return true
} }
query := m.GetSignedInUserQuery{UserId: user.Id} query := m.GetSignedInUserQuery{UserId: user.Id, OrgId: orgId}
if err := bus.Dispatch(&query); err != nil { if err := bus.Dispatch(&query); err != nil {
ctx.JsonApiErr(401, "Authentication error", err) ctx.JsonApiErr(401, "Authentication error", err)
return true return true
} else {
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
return true
} }
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
return true
} }
// Handle handles and logs error by given status. // Handle handles and logs error by given status.
......
...@@ -117,6 +117,7 @@ type GetSignedInUserQuery struct { ...@@ -117,6 +117,7 @@ type GetSignedInUserQuery struct {
UserId int64 UserId int64
Login string Login string
Email string Email string
OrgId int64
Result *SignedInUser Result *SignedInUser
} }
......
package sqlstore package sqlstore
import ( import (
"strconv"
"strings" "strings"
"time" "time"
...@@ -273,7 +274,7 @@ func SetUsingOrg(cmd *m.SetUsingOrgCommand) error { ...@@ -273,7 +274,7 @@ func SetUsingOrg(cmd *m.SetUsingOrgCommand) error {
} }
if !valid { if !valid {
return fmt.Errorf("user does not belong ot org") return fmt.Errorf("user does not belong to org")
} }
return inTransaction(func(sess *xorm.Session) error { return inTransaction(func(sess *xorm.Session) error {
...@@ -319,19 +320,24 @@ func GetUserOrgList(query *m.GetUserOrgListQuery) error { ...@@ -319,19 +320,24 @@ func GetUserOrgList(query *m.GetUserOrgListQuery) error {
} }
func GetSignedInUser(query *m.GetSignedInUserQuery) error { func GetSignedInUser(query *m.GetSignedInUserQuery) error {
orgId := "u.org_id"
if query.OrgId > 0 {
orgId = strconv.FormatInt(query.OrgId, 10)
}
var rawSql = `SELECT var rawSql = `SELECT
u.id as user_id, u.id as user_id,
u.is_admin as is_grafana_admin, u.is_admin as is_grafana_admin,
u.email as email, u.email as email,
u.login as login, u.login as login,
u.name as name, u.name as name,
u.help_flags1 as help_flags1, u.help_flags1 as help_flags1,
org.name as org_name, org.name as org_name,
org_user.role as org_role, org_user.role as org_role,
org.id as org_id org.id as org_id
FROM ` + dialect.Quote("user") + ` as u FROM ` + dialect.Quote("user") + ` as u
LEFT OUTER JOIN org_user on org_user.org_id = u.org_id and org_user.user_id = u.id LEFT OUTER JOIN org_user on org_user.org_id = ` + orgId + ` and org_user.user_id = u.id
LEFT OUTER JOIN org on org.id = u.org_id ` LEFT OUTER JOIN org on org.id = org_user.org_id `
sess := x.Table("user") sess := x.Table("user")
if query.UserId > 0 { if query.UserId > 0 {
......
...@@ -9,8 +9,8 @@ export class BackendSrv { ...@@ -9,8 +9,8 @@ export class BackendSrv {
inFlightRequests = {}; inFlightRequests = {};
HTTP_REQUEST_CANCELLED = -1; HTTP_REQUEST_CANCELLED = -1;
/** @ngInject */ /** @ngInject */
constructor(private $http, private alertSrv, private $rootScope, private $q, private $timeout) { constructor(private $http, private alertSrv, private $rootScope, private $q, private $timeout, private contextSrv) {
} }
get(url, params?) { get(url, params?) {
...@@ -63,12 +63,18 @@ export class BackendSrv { ...@@ -63,12 +63,18 @@ export class BackendSrv {
request(options) { request(options) {
options.retry = options.retry || 0; options.retry = options.retry || 0;
var requestIsLocal = options.url.indexOf('/') === 0; var requestIsLocal = !options.url.match(/^http/);
var firstAttempt = options.retry === 0; var firstAttempt = options.retry === 0;
if (requestIsLocal && !options.hasSubUrl) { if (requestIsLocal) {
options.url = config.appSubUrl + options.url; if (this.contextSrv.user && this.contextSrv.user.orgId) {
options.hasSubUrl = true; options.headers = options.headers || {};
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
}
if (options.url.indexOf("/") === 0) {
options.url = options.url.substring(1);
}
} }
return this.$http(options).then(results => { return this.$http(options).then(results => {
...@@ -125,16 +131,23 @@ export class BackendSrv { ...@@ -125,16 +131,23 @@ export class BackendSrv {
this.addCanceler(requestId, canceler); this.addCanceler(requestId, canceler);
} }
var requestIsLocal = options.url.indexOf('/') === 0; var requestIsLocal = !options.url.match(/^http/);
var firstAttempt = options.retry === 0; var firstAttempt = options.retry === 0;
if (requestIsLocal && !options.hasSubUrl && options.retry === 0) { if (requestIsLocal) {
options.url = config.appSubUrl + options.url; if (this.contextSrv.user && this.contextSrv.user.orgId) {
} options.headers = options.headers || {};
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
}
if (options.url.indexOf("/") === 0) {
options.url = options.url.substring(1);
}
if (requestIsLocal && options.headers && options.headers.Authorization) { if (options.headers && options.headers.Authorization) {
options.headers['X-DS-Authorization'] = options.headers.Authorization; options.headers['X-DS-Authorization'] = options.headers.Authorization;
delete options.headers.Authorization; delete options.headers.Authorization;
}
} }
return this.$http(options).catch(err => { return this.$http(options).catch(err => {
......
...@@ -89,7 +89,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -89,7 +89,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
var intervalFactor = target.intervalFactor || 1; var intervalFactor = target.intervalFactor || 1;
target.step = query.step = this.calculateInterval(interval, intervalFactor); target.step = query.step = this.calculateInterval(interval, intervalFactor);
var range = Math.ceil(end - start); var range = Math.ceil(end - start);
target.step = query.step = this.adjustStep(query.step, range); target.step = query.step = this.adjustStep(query.step, this.intervalSeconds(options.interval), range);
queries.push(query); queries.push(query);
}); });
...@@ -122,13 +122,13 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -122,13 +122,13 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
}); });
}; };
this.adjustStep = function(step, range) { this.adjustStep = function(step, autoStep, range) {
// Prometheus drop query if range/step > 11000 // Prometheus drop query if range/step > 11000
// calibrate step if it is too big // calibrate step if it is too big
if (step !== 0 && range / step > 11000) { if (step !== 0 && range / step > 11000) {
return Math.ceil(range / 11000); return Math.ceil(range / 11000);
} }
return step; return Math.max(step, autoStep);
}; };
this.performTimeSeriesQuery = function(query, start, end) { this.performTimeSeriesQuery = function(query, start, end) {
...@@ -189,7 +189,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -189,7 +189,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
var end = this.getPrometheusTime(options.range.to, true); var end = this.getPrometheusTime(options.range.to, true);
var query = { var query = {
expr: interpolated, expr: interpolated,
step: this.adjustStep(kbn.interval_to_seconds(step), Math.ceil(end - start)) + 's' step: this.adjustStep(kbn.interval_to_seconds(step), 0, Math.ceil(end - start)) + 's'
}; };
var self = this; var self = this;
...@@ -229,6 +229,10 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -229,6 +229,10 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
}; };
this.calculateInterval = function(interval, intervalFactor) { this.calculateInterval = function(interval, intervalFactor) {
return Math.ceil(this.intervalSeconds(interval) * intervalFactor);
};
this.intervalSeconds = function(interval) {
var m = interval.match(durationSplitRegexp); var m = interval.match(durationSplitRegexp);
var dur = moment.duration(parseInt(m[1]), m[2]); var dur = moment.duration(parseInt(m[1]), m[2]);
var sec = dur.asSeconds(); var sec = dur.asSeconds();
...@@ -236,7 +240,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -236,7 +240,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
sec = 1; sec = 1;
} }
return Math.ceil(sec * intervalFactor); return sec;
}; };
this.transformMetricData = function(md, options, start, end) { this.transformMetricData = function(md, options, start, end) {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
</input> </input>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-5">Step</label> <label class="gf-form-label">Min step</label>
<input type="text" class="gf-form-input max-width-5" ng-model="ctrl.target.interval" <input type="text" class="gf-form-input max-width-5" ng-model="ctrl.target.interval"
data-placement="right" data-placement="right"
spellcheck='false' spellcheck='false'
......
...@@ -58,7 +58,7 @@ class GettingStartedPanelCtrl extends PanelCtrl { ...@@ -58,7 +58,7 @@ class GettingStartedPanelCtrl extends PanelCtrl {
icon: 'icon-gf icon-gf-users', icon: 'icon-gf icon-gf-users',
href: 'org/users?gettingstarted', href: 'org/users?gettingstarted',
check: () => { check: () => {
return this.backendSrv.get('api/org/users').then(res => { return this.backendSrv.get('/api/org/users').then(res => {
return res.length > 1; return res.length > 1;
}); });
} }
...@@ -71,7 +71,7 @@ class GettingStartedPanelCtrl extends PanelCtrl { ...@@ -71,7 +71,7 @@ class GettingStartedPanelCtrl extends PanelCtrl {
icon: 'icon-gf icon-gf-apps', icon: 'icon-gf icon-gf-apps',
href: 'https://grafana.com/plugins?utm_source=grafana_getting_started', href: 'https://grafana.com/plugins?utm_source=grafana_getting_started',
check: () => { check: () => {
return this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => { return this.backendSrv.get('/api/plugins', {embedded: 0, core: 0}).then(plugins => {
return plugins.length > 0; return plugins.length > 0;
}); });
} }
......
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