From 43ac79685ad9fa16593c9b0ce103058292660a17 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 31 Jan 2019 15:45:11 +0100 Subject: [PATCH 01/24] delete auth token on signout --- pkg/api/common_test.go | 2 +- pkg/middleware/middleware_test.go | 2 +- pkg/services/auth/auth_token.go | 26 +++++++++++++++++++++++--- pkg/services/auth/auth_token_test.go | 25 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index eb1f89e3f22..f6c6e53e91d 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -149,4 +149,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) {} +func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 11740574d0b..9bb45062e00 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -602,4 +602,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) {} +func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index 7e9433c2d70..d9c5e897f70 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -3,6 +3,7 @@ package auth import ( "crypto/sha256" "encoding/hex" + "errors" "net/http" "net/url" "time" @@ -31,7 +32,7 @@ var ( type UserAuthTokenService interface { InitContextWithToken(ctx *models.ReqContext, orgID int64) bool UserAuthenticatedHook(user *models.User, c *models.ReqContext) error - UserSignedOutHook(c *models.ReqContext) + UserSignedOutHook(c *models.ReqContext) error } type UserAuthTokenServiceImpl struct { @@ -111,8 +112,27 @@ func (s *UserAuthTokenServiceImpl) UserAuthenticatedHook(user *models.User, c *m return nil } -func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) { - s.writeSessionCookie(c, "", -1) +func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) error { + unhashedToken := c.GetCookie(s.Cfg.LoginCookieName) + if unhashedToken == "" { + return errors.New("cannot logout without session token") + } + + hashedToken := hashToken(unhashedToken) + + sql := `DELETE FROM user_auth_token WHERE auth_token = ?` + res, err := s.SQLStore.NewSession().Exec(sql, hashedToken) + if err != nil { + return err + } + + affected, _ := res.RowsAffected() + if affected > 0 { + s.writeSessionCookie(c, "", -1) + return nil + } + + return errors.New("failed to delete session") } func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) { diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 2f75c660d9d..0114939ea48 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -1,10 +1,13 @@ package auth import ( + "net/http" "testing" "time" + "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -46,6 +49,28 @@ func TestUserAuthToken(t *testing.T) { So(err, ShouldEqual, ErrAuthTokenNotFound) So(LookupToken, ShouldBeNil) }) + + Convey("signing out should delete token and cookie if present", func() { + token, err := userAuthTokenService.CreateToken(userID, "192.168.1.1:1234", "some user agent2") + So(err, ShouldBeNil) + So(token, ShouldNotBeNil) + + httpreq := &http.Request{Header: make(http.Header)} + httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: token.AuthToken}) + + ctx := &models.ReqContext{Context: &macaron.Context{Req: macaron.Request{Request: httpreq}}} + + err = userAuthTokenService.UserSignedOutHook(ctx) + So(err, ShouldBeNil) + + // makes sure we tell the browser to overwrite the cookie + So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, "") + + // lookedUp, err = userAuthTokenService.LookupToken(token.UnhashedToken) + // So(err, ShouldBeNil) + // So(lookedUp, ShouldNotBeNil) + + }) }) Convey("expires correctly", func() { From 88ca54eba96195d1fc0e0138c17d8c6991deb938 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 31 Jan 2019 16:22:40 +0100 Subject: [PATCH 02/24] renames signout function --- pkg/api/common_test.go | 2 +- pkg/api/login.go | 2 +- pkg/middleware/middleware_test.go | 2 +- pkg/services/auth/auth_token.go | 4 ++-- pkg/services/auth/auth_token_test.go | 11 +++-------- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index f6c6e53e91d..fe02c94e277 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -149,4 +149,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } +func (s *fakeUserAuthTokenService) SignOutUser(c *m.ReqContext) error { return nil } diff --git a/pkg/api/login.go b/pkg/api/login.go index 50c62e0835a..49da147724e 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -136,7 +136,7 @@ func (hs *HTTPServer) loginUserWithUser(user *m.User, c *m.ReqContext) { } func (hs *HTTPServer) Logout(c *m.ReqContext) { - hs.AuthTokenService.UserSignedOutHook(c) + hs.AuthTokenService.SignOutUser(c) if setting.SignoutRedirectUrl != "" { c.Redirect(setting.SignoutRedirectUrl) diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 9bb45062e00..4679c449853 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -602,4 +602,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } +func (s *fakeUserAuthTokenService) SignOutUser(c *m.ReqContext) error { return nil } diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index d9c5e897f70..5f8f36fc373 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -32,7 +32,7 @@ var ( type UserAuthTokenService interface { InitContextWithToken(ctx *models.ReqContext, orgID int64) bool UserAuthenticatedHook(user *models.User, c *models.ReqContext) error - UserSignedOutHook(c *models.ReqContext) error + SignOutUser(c *models.ReqContext) error } type UserAuthTokenServiceImpl struct { @@ -112,7 +112,7 @@ func (s *UserAuthTokenServiceImpl) UserAuthenticatedHook(user *models.User, c *m return nil } -func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) error { +func (s *UserAuthTokenServiceImpl) SignOutUser(c *models.ReqContext) error { unhashedToken := c.GetCookie(s.Cfg.LoginCookieName) if unhashedToken == "" { return errors.New("cannot logout without session token") diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 0114939ea48..47afe627479 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -51,7 +51,7 @@ func TestUserAuthToken(t *testing.T) { }) Convey("signing out should delete token and cookie if present", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.1.1:1234", "some user agent2") + token, err := userAuthTokenService.CreateToken(userID, "192.168.1.1:1234", "user agent") So(err, ShouldBeNil) So(token, ShouldNotBeNil) @@ -60,16 +60,11 @@ func TestUserAuthToken(t *testing.T) { ctx := &models.ReqContext{Context: &macaron.Context{Req: macaron.Request{Request: httpreq}}} - err = userAuthTokenService.UserSignedOutHook(ctx) + err = userAuthTokenService.SignOutUser(ctx) So(err, ShouldBeNil) // makes sure we tell the browser to overwrite the cookie - So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, "") - - // lookedUp, err = userAuthTokenService.LookupToken(token.UnhashedToken) - // So(err, ShouldBeNil) - // So(lookedUp, ShouldNotBeNil) - + //So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, "") }) }) From 0442a86400552f461f7c8a807a1f2fe609fd5e98 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 31 Jan 2019 19:22:25 +0100 Subject: [PATCH 03/24] tailing grafana logs and temporaily using an older build --- devenv/docker/blocks/loki/config.yaml | 27 +++++++++++++++++++ devenv/docker/blocks/loki/docker-compose.yaml | 6 +++-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 devenv/docker/blocks/loki/config.yaml diff --git a/devenv/docker/blocks/loki/config.yaml b/devenv/docker/blocks/loki/config.yaml new file mode 100644 index 00000000000..9451b6ba79b --- /dev/null +++ b/devenv/docker/blocks/loki/config.yaml @@ -0,0 +1,27 @@ +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +client: + url: http://loki:3100/api/prom/push + +scrape_configs: +- job_name: system + entry_parser: raw + static_configs: + - targets: + - localhost + labels: + job: varlogs + __path__: /var/log/*log +- job_name: grafana + entry_parser: raw + static_configs: + - targets: + - localhost + labels: + job: grafana + __path__: /var/log/grafana/*log diff --git a/devenv/docker/blocks/loki/docker-compose.yaml b/devenv/docker/blocks/loki/docker-compose.yaml index d6cf21f7856..c2fee15b0bb 100644 --- a/devenv/docker/blocks/loki/docker-compose.yaml +++ b/devenv/docker/blocks/loki/docker-compose.yaml @@ -5,7 +5,7 @@ networks: services: loki: - image: grafana/loki:master + image: grafana/loki:master-3e6a75e ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml @@ -13,9 +13,11 @@ services: - loki promtail: - image: grafana/promtail:master + image: grafana/promtail:master-3e6a75e volumes: + - ./docker/blocks/loki/config.yaml:/etc/promtail/docker-config.yaml - /var/log:/var/log + - ../data/log:/var/log/grafana command: -config.file=/etc/promtail/docker-config.yaml networks: From f9bab9585a68bb65201a9f7e5c15052601546e22 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 31 Jan 2019 19:38:49 +0100 Subject: [PATCH 04/24] wip --- public/app/core/utils/explore.test.ts | 9 ++++++++- public/app/core/utils/explore.ts | 19 ++++++++++++++++--- public/app/types/explore.ts | 7 +++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index 32135eab90a..d818b2ef090 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -13,6 +13,11 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = { datasource: null, queries: [], range: DEFAULT_RANGE, + ui: { + showingGraph: true, + showingTable: true, + showingLogs: true, + } }; describe('state functions', () => { @@ -69,9 +74,11 @@ describe('state functions', () => { to: 'now', }, }; + expect(serializeStateToUrlParam(state)).toBe( '{"datasource":"foo","queries":[{"expr":"metric{test=\\"a/b\\"}"},' + - '{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}' + '{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"},' + + '"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}' ); }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 7a9f54a0cae..07c8cf1d24b 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -20,6 +20,7 @@ import { ResultType, QueryIntervals, QueryOptions, + ExploreUrlUIState, } from 'app/types/explore'; export const DEFAULT_RANGE = { @@ -27,6 +28,12 @@ export const DEFAULT_RANGE = { to: 'now', }; +export const DEFAULT_UI_STATE = { + showingTable: true, + showingGraph: true, + showingLogs: true, +}; + const MAX_HISTORY_ITEMS = 100; export const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource'; @@ -151,6 +158,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { if (initial) { try { const parsed = JSON.parse(decodeURI(initial)); + // debugger if (Array.isArray(parsed)) { if (parsed.length <= 3) { throw new Error('Error parsing compact URL state for Explore.'); @@ -161,19 +169,24 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { }; const datasource = parsed[2]; const queries = parsed.slice(3); - return { datasource, queries, range }; + return { datasource, queries, range, ui: DEFAULT_UI_STATE }; } return parsed; } catch (e) { console.error(e); } } - return { datasource: null, queries: [], range: DEFAULT_RANGE }; + return { datasource: null, queries: [], range: DEFAULT_RANGE, ui: DEFAULT_UI_STATE }; } +const serializeUIState = (state: ExploreUrlUIState) => { + return Object.keys(state).map((key) => ({ [key]: state[key] })); +}; + export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: boolean): string { + if (compact) { - return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.queries]); + return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.queries, ...serializeUIState(urlState.ui)]); } return JSON.stringify(urlState); } diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 34b7ff08c99..d035b60d86a 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -231,10 +231,17 @@ export interface ExploreItemState { tableResult?: TableModel; } +export interface ExploreUrlUIState { + showingTable: boolean; + showingGraph: boolean; + showingLogs: boolean; +} + export interface ExploreUrlState { datasource: string; queries: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense range: RawTimeRange; + ui: ExploreUrlUIState; } export interface HistoryItem { From 91bd908e03ecdfbc691c13e66cb9512535ca78fb Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 31 Jan 2019 22:24:04 +0100 Subject: [PATCH 05/24] adds more tests signing out session --- pkg/services/auth/auth_token.go | 2 +- pkg/services/auth/auth_token_test.go | 35 +++++++++++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index 5f8f36fc373..deb3c1a5bba 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -86,7 +86,7 @@ func (s *UserAuthTokenServiceImpl) InitContextWithToken(ctx *models.ReqContext, func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) { if setting.Env == setting.DEV { - ctx.Logger.Info("new token", "unhashed token", value) + ctx.Logger.Debug("new token", "unhashed token", value) } ctx.Resp.Header().Del("Set-Cookie") diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 47afe627479..e58fe795b4a 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -1,13 +1,15 @@ package auth import ( + "fmt" "net/http" + "net/http/httptest" "testing" "time" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" - "gopkg.in/macaron.v1" + macaron "gopkg.in/macaron.v1" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -51,20 +53,37 @@ func TestUserAuthToken(t *testing.T) { }) Convey("signing out should delete token and cookie if present", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.1.1:1234", "user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - httpreq := &http.Request{Header: make(http.Header)} - httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: token.AuthToken}) + httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: token.UnhashedToken}) - ctx := &models.ReqContext{Context: &macaron.Context{Req: macaron.Request{Request: httpreq}}} + ctx := &models.ReqContext{Context: &macaron.Context{ + Req: macaron.Request{Request: httpreq}, + Resp: macaron.NewResponseWriter("POST", httptest.NewRecorder()), + }, + Logger: log.New("fakelogger"), + } err = userAuthTokenService.SignOutUser(ctx) So(err, ShouldBeNil) // makes sure we tell the browser to overwrite the cookie - //So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, "") + cookieHeader := fmt.Sprintf("%s=; Path=/; Max-Age=0; HttpOnly", userAuthTokenService.Cfg.LoginCookieName) + So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, cookieHeader) + }) + + Convey("signing out an none existing session should return an error", func() { + httpreq := &http.Request{Header: make(http.Header)} + httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: "missing-session-cookie"}) + + ctx := &models.ReqContext{Context: &macaron.Context{ + Req: macaron.Request{Request: httpreq}, + Resp: macaron.NewResponseWriter("POST", httptest.NewRecorder()), + }, + Logger: log.New("fakelogger"), + } + + err = userAuthTokenService.SignOutUser(ctx) + So(err, ShouldNotBeNil) }) }) From dd5a8275f107dbc54160fc271c3230096ba3707b Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 1 Feb 2019 01:21:23 +0100 Subject: [PATCH 06/24] must return json response from /api/login/ping Even though http error 401 was returned, the result was still a http 200 --- pkg/api/login.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/api/login.go b/pkg/api/login.go index 50c62e0835a..3f2d82a6c0f 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -78,12 +78,13 @@ func tryOAuthAutoLogin(c *m.ReqContext) bool { return false } -func (hs *HTTPServer) LoginAPIPing(c *m.ReqContext) Response { - if c.IsSignedIn || c.IsAnonymous { - return JSON(200, "Logged in") +func (hs *HTTPServer) LoginAPIPing(c *m.ReqContext) { + if c.IsSignedIn || (c.AllowAnonymous && c.IsAnonymous) { + c.JsonOK("Logged in") + return } - return Error(401, "Unauthorized", nil) + c.JsonApiErr(401, "Unauthorized", nil) } func (hs *HTTPServer) LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response { From bd830780250880f3c9920a5c1f5c467b3e5bdb2a Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 1 Feb 2019 01:22:56 +0100 Subject: [PATCH 07/24] signout user if /api/login/ping returns 401 unauthorized --- public/app/core/services/backend_srv.ts | 37 +++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 38d7f2b76cb..c73cc7661f5 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; +import config from 'app/core/config'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; export class BackendSrv { @@ -103,10 +104,17 @@ export class BackendSrv { err => { // handle unauthorized if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) { - return this.loginPing().then(() => { - options.retry = 1; - return this.request(options); - }); + return this.loginPing() + .then(() => { + options.retry = 1; + return this.request(options); + }) + .catch(err => { + if (err.status === 401) { + window.location.href = config.appSubUrl + '/logout'; + throw err; + } + }); } this.$timeout(this.requestErrorHandler.bind(this, err), 50); @@ -184,13 +192,20 @@ export class BackendSrv { // handle unauthorized for backend requests if (requestIsLocal && firstAttempt && err.status === 401) { - return this.loginPing().then(() => { - options.retry = 1; - if (canceler) { - canceler.resolve(); - } - return this.datasourceRequest(options); - }); + return this.loginPing() + .then(() => { + options.retry = 1; + if (canceler) { + canceler.resolve(); + } + return this.datasourceRequest(options); + }) + .catch(err => { + if (err.status === 401) { + window.location.href = config.appSubUrl + '/logout'; + throw err; + } + }); } // populate error obj on Internal Error From a1b3986532dbbb51145471304fccc6f254899bfe Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 1 Feb 2019 09:59:53 +0100 Subject: [PATCH 08/24] always delete session cookie even if db delete fails --- devenv/docker/blocks/loki/docker-compose.yaml | 2 ++ pkg/services/auth/auth_token.go | 14 +++----------- pkg/services/auth/auth_token_test.go | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/devenv/docker/blocks/loki/docker-compose.yaml b/devenv/docker/blocks/loki/docker-compose.yaml index d6cf21f7856..bd4f8d3c728 100644 --- a/devenv/docker/blocks/loki/docker-compose.yaml +++ b/devenv/docker/blocks/loki/docker-compose.yaml @@ -20,3 +20,5 @@ services: -config.file=/etc/promtail/docker-config.yaml networks: - loki + depends_on: + - loki diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index deb3c1a5bba..5cb43974d34 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -121,18 +121,10 @@ func (s *UserAuthTokenServiceImpl) SignOutUser(c *models.ReqContext) error { hashedToken := hashToken(unhashedToken) sql := `DELETE FROM user_auth_token WHERE auth_token = ?` - res, err := s.SQLStore.NewSession().Exec(sql, hashedToken) - if err != nil { - return err - } + _, err := s.SQLStore.NewSession().Exec(sql, hashedToken) - affected, _ := res.RowsAffected() - if affected > 0 { - s.writeSessionCookie(c, "", -1) - return nil - } - - return errors.New("failed to delete session") + s.writeSessionCookie(c, "", -1) + return err } func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) { diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index e58fe795b4a..312e53a3970 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -73,7 +73,7 @@ func TestUserAuthToken(t *testing.T) { Convey("signing out an none existing session should return an error", func() { httpreq := &http.Request{Header: make(http.Header)} - httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: "missing-session-cookie"}) + httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: ""}) ctx := &models.ReqContext{Context: &macaron.Context{ Req: macaron.Request{Request: httpreq}, From 6ab9355146193941c4e719e8cfa43af7f382179e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 1 Feb 2019 12:33:15 +0100 Subject: [PATCH 09/24] Restoring explore panels state from URL --- public/app/core/utils/explore.test.ts | 23 +++++- public/app/core/utils/explore.ts | 38 +++++++--- public/app/features/explore/Explore.tsx | 10 ++- .../app/features/explore/state/actionTypes.ts | 2 + public/app/features/explore/state/actions.ts | 73 ++++++++++++------- public/app/features/explore/state/reducers.ts | 3 +- public/app/types/explore.ts | 4 +- 7 files changed, 109 insertions(+), 44 deletions(-) diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index d818b2ef090..1c00142c3b8 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -100,7 +100,7 @@ describe('state functions', () => { }, }; expect(serializeStateToUrlParam(state, true)).toBe( - '["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"}]' + '["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true]}]' ); }); }); @@ -125,7 +125,28 @@ describe('state functions', () => { }; const serialized = serializeStateToUrlParam(state); const parsed = parseUrlState(serialized); + expect(state).toMatchObject(parsed); + }); + it('can parse the compact serialized state into the original state', () => { + const state = { + ...DEFAULT_EXPLORE_STATE, + datasource: 'foo', + queries: [ + { + expr: 'metric{test="a/b"}', + }, + { + expr: 'super{foo="x/z"}', + }, + ], + range: { + from: 'now - 5h', + to: 'now', + }, + }; + const serialized = serializeStateToUrlParam(state, true); + const parsed = parseUrlState(serialized); expect(state).toMatchObject(parsed); }); }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 07c8cf1d24b..7128019b1fb 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -20,7 +20,6 @@ import { ResultType, QueryIntervals, QueryOptions, - ExploreUrlUIState, } from 'app/types/explore'; export const DEFAULT_RANGE = { @@ -154,11 +153,13 @@ export function buildQueryTransaction( export const clearQueryKeys: ((query: DataQuery) => object) = ({ key, refId, ...rest }) => rest; +const isMetricSegment = (segment: { [key: string]: string }) => segment.hasOwnProperty('expr'); +const isUISegment = (segment: { [key: string]: string }) => segment.hasOwnProperty('ui'); + export function parseUrlState(initial: string | undefined): ExploreUrlState { if (initial) { try { const parsed = JSON.parse(decodeURI(initial)); - // debugger if (Array.isArray(parsed)) { if (parsed.length <= 3) { throw new Error('Error parsing compact URL state for Explore.'); @@ -168,8 +169,24 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { to: parsed[1], }; const datasource = parsed[2]; - const queries = parsed.slice(3); - return { datasource, queries, range, ui: DEFAULT_UI_STATE }; + let queries = [], + ui; + + parsed.slice(3).forEach(segment => { + if (isMetricSegment(segment)) { + queries = [...queries, segment]; + } + + if (isUISegment(segment)) { + ui = { + showingGraph: segment.ui[0], + showingLogs: segment.ui[1], + showingTable: segment.ui[2], + }; + } + }); + + return { datasource, queries, range, ui }; } return parsed; } catch (e) { @@ -179,14 +196,15 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { return { datasource: null, queries: [], range: DEFAULT_RANGE, ui: DEFAULT_UI_STATE }; } -const serializeUIState = (state: ExploreUrlUIState) => { - return Object.keys(state).map((key) => ({ [key]: state[key] })); -}; - export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: boolean): string { - if (compact) { - return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.queries, ...serializeUIState(urlState.ui)]); + return JSON.stringify([ + urlState.range.from, + urlState.range.to, + urlState.datasource, + ...urlState.queries, + { ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable] }, + ]); } return JSON.stringify(urlState); } diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 909c4e81b8b..d08243c7118 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -32,7 +32,7 @@ import { import { RawTimeRange, TimeRange, DataQuery } from '@grafana/ui'; import { ExploreItemState, ExploreUrlState, RangeScanner, ExploreId } from 'app/types/explore'; import { StoreState } from 'app/types'; -import { LAST_USED_DATASOURCE_KEY, ensureQueries, DEFAULT_RANGE } from 'app/core/utils/explore'; +import { LAST_USED_DATASOURCE_KEY, ensureQueries, DEFAULT_RANGE, DEFAULT_UI_STATE } from 'app/core/utils/explore'; import { Emitter } from 'app/core/utils/emitter'; import { ExploreToolbar } from './ExploreToolbar'; @@ -61,7 +61,7 @@ interface ExploreProps { supportsGraph: boolean | null; supportsLogs: boolean | null; supportsTable: boolean | null; - urlState: ExploreUrlState; + urlState?: ExploreUrlState; } /** @@ -107,18 +107,20 @@ export class Explore extends React.PureComponent { // Don't initialize on split, but need to initialize urlparameters when present if (!initialized) { // Load URL state and parse range - const { datasource, queries, range = DEFAULT_RANGE } = (urlState || {}) as ExploreUrlState; + const { datasource, queries, range = DEFAULT_RANGE, ui = DEFAULT_UI_STATE } = (urlState || {}) as ExploreUrlState; const initialDatasource = datasource || store.get(LAST_USED_DATASOURCE_KEY); const initialQueries: DataQuery[] = ensureQueries(queries); const initialRange = { from: parseTime(range.from), to: parseTime(range.to) }; const width = this.el ? this.el.offsetWidth : 0; + this.props.initializeExplore( exploreId, initialDatasource, initialQueries, initialRange, width, - this.exploreEvents + this.exploreEvents, + ui ); } } diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts index be7d5754bbe..3a0a564b651 100644 --- a/public/app/features/explore/state/actionTypes.ts +++ b/public/app/features/explore/state/actionTypes.ts @@ -8,6 +8,7 @@ import { RangeScanner, ResultType, QueryTransaction, + ExploreUIState, } from 'app/types/explore'; export enum ActionTypes { @@ -106,6 +107,7 @@ export interface InitializeExploreAction { exploreDatasources: DataSourceSelectItem[]; queries: DataQuery[]; range: RawTimeRange; + ui: ExploreUIState; }; } diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 1a11b7fcac9..02502a1d94c 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -38,6 +38,7 @@ import { ResultType, QueryOptions, QueryTransaction, + ExploreUIState, } from 'app/types/explore'; import { @@ -154,7 +155,8 @@ export function initializeExplore( queries: DataQuery[], range: RawTimeRange, containerWidth: number, - eventBridge: Emitter + eventBridge: Emitter, + ui: ExploreUIState ): ThunkResult { return async dispatch => { const exploreDatasources: DataSourceSelectItem[] = getDatasourceSrv() @@ -175,6 +177,7 @@ export function initializeExplore( exploreDatasources, queries, range, + ui, }, }); @@ -258,10 +261,7 @@ export const queriesImported = (exploreId: ExploreId, queries: DataQuery[]): Que * run datasource-specific code. Existing queries are imported to the new datasource if an importer exists, * e.g., Prometheus -> Loki queries. */ -export const loadDatasourceSuccess = ( - exploreId: ExploreId, - instance: any, -): LoadDatasourceSuccessAction => { +export const loadDatasourceSuccess = (exploreId: ExploreId, instance: any): LoadDatasourceSuccessAction => { // Capabilities const supportsGraph = instance.meta.metrics; const supportsLogs = instance.meta.logs; @@ -766,6 +766,11 @@ export function stateSave() { datasource: left.datasourceInstance.name, queries: left.modifiedQueries.map(clearQueryKeys), range: left.range, + ui: { + showingGraph: left.showingGraph, + showingLogs: left.showingLogs, + showingTable: left.showingTable, + }, }; urlStates.left = serializeStateToUrlParam(leftUrlState, true); if (split) { @@ -773,48 +778,64 @@ export function stateSave() { datasource: right.datasourceInstance.name, queries: right.modifiedQueries.map(clearQueryKeys), range: right.range, + ui: { + showingGraph: right.showingGraph, + showingLogs: right.showingLogs, + showingTable: right.showingTable, + }, }; + urlStates.right = serializeStateToUrlParam(rightUrlState, true); } + dispatch(updateLocation({ query: urlStates })); }; } /** - * Expand/collapse the graph result viewer. When collapsed, graph queries won't be run. + * Creates action to collapse graph/logs/table panel. When panel is collapsed, + * queries won't be run */ -export function toggleGraph(exploreId: ExploreId): ThunkResult { +const togglePanelActionCreator = (type: ActionTypes.ToggleGraph | ActionTypes.ToggleTable | ActionTypes.ToggleLogs) => ( + exploreId: ExploreId +) => { return (dispatch, getState) => { - dispatch({ type: ActionTypes.ToggleGraph, payload: { exploreId } }); - if (getState().explore[exploreId].showingGraph) { + let shouldRunQueries; + dispatch({ type, payload: { exploreId } }); + dispatch(stateSave()); + + switch (type) { + case ActionTypes.ToggleGraph: + shouldRunQueries = getState().explore[exploreId].showingGraph; + break; + case ActionTypes.ToggleLogs: + shouldRunQueries = getState().explore[exploreId].showingLogs; + break; + case ActionTypes.ToggleTable: + shouldRunQueries = getState().explore[exploreId].showingTable; + break; + } + + if (shouldRunQueries) { dispatch(runQueries(exploreId)); } }; -} +}; + +/** + * Expand/collapse the graph result viewer. When collapsed, graph queries won't be run. + */ +export const toggleGraph = togglePanelActionCreator(ActionTypes.ToggleGraph); /** * Expand/collapse the logs result viewer. When collapsed, log queries won't be run. */ -export function toggleLogs(exploreId: ExploreId): ThunkResult { - return (dispatch, getState) => { - dispatch({ type: ActionTypes.ToggleLogs, payload: { exploreId } }); - if (getState().explore[exploreId].showingLogs) { - dispatch(runQueries(exploreId)); - } - }; -} +export const toggleLogs = togglePanelActionCreator(ActionTypes.ToggleLogs); /** * Expand/collapse the table result viewer. When collapsed, table queries won't be run. */ -export function toggleTable(exploreId: ExploreId): ThunkResult { - return (dispatch, getState) => { - dispatch({ type: ActionTypes.ToggleTable, payload: { exploreId } }); - if (getState().explore[exploreId].showingTable) { - dispatch(runQueries(exploreId)); - } - }; -} +export const toggleTable = togglePanelActionCreator(ActionTypes.ToggleTable); /** * Resets state for explore. diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index eb67beee3b3..4ad07ddfc88 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -163,7 +163,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => { } case ActionTypes.InitializeExplore: { - const { containerWidth, eventBridge, exploreDatasources, queries, range } = action.payload; + const { containerWidth, eventBridge, exploreDatasources, queries, range, ui } = action.payload; return { ...state, containerWidth, @@ -173,6 +173,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => { initialQueries: queries, initialized: true, modifiedQueries: queries.slice(), + ...ui, }; } diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index d035b60d86a..3abbc652c0d 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -231,7 +231,7 @@ export interface ExploreItemState { tableResult?: TableModel; } -export interface ExploreUrlUIState { +export interface ExploreUIState { showingTable: boolean; showingGraph: boolean; showingLogs: boolean; @@ -241,7 +241,7 @@ export interface ExploreUrlState { datasource: string; queries: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense range: RawTimeRange; - ui: ExploreUrlUIState; + ui: ExploreUIState; } export interface HistoryItem { From 2ddccb4a214a1828bde0ffb3c0d0191773d8146a Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 1 Feb 2019 12:57:09 +0100 Subject: [PATCH 10/24] Temporarily run queries independently from UI state of explore panels --- public/app/features/explore/state/actions.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 02502a1d94c..b24532c23f4 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -577,9 +577,9 @@ export function runQueries(exploreId: ExploreId) { const { datasourceInstance, modifiedQueries, - showingLogs, - showingGraph, - showingTable, + // showingLogs, + // showingGraph, + // showingTable, supportsGraph, supportsLogs, supportsTable, @@ -596,7 +596,7 @@ export function runQueries(exploreId: ExploreId) { const interval = datasourceInstance.interval; // Keep table queries first since they need to return quickly - if (showingTable && supportsTable) { + if (/*showingTable &&*/ supportsTable) { dispatch( runQueriesForType( exploreId, @@ -611,7 +611,7 @@ export function runQueries(exploreId: ExploreId) { ) ); } - if (showingGraph && supportsGraph) { + if (/*showingGraph &&*/ supportsGraph) { dispatch( runQueriesForType( exploreId, @@ -625,7 +625,7 @@ export function runQueries(exploreId: ExploreId) { ) ); } - if (showingLogs && supportsLogs) { + if (/*showingLogs &&*/ supportsLogs) { dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' })); } dispatch(stateSave()); From 3c358e406e20f3a47b9d49fecadd53a6a2259843 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 1 Feb 2019 14:56:54 +0100 Subject: [PATCH 11/24] Make runQueries action independent from datasource loading --- public/app/features/explore/state/actions.ts | 40 ++++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index b24532c23f4..c7b47d1c3c7 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -79,7 +79,15 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun await dispatch(importQueries(exploreId, modifiedQueries, currentDataSourceInstance, newDataSourceInstance)); dispatch(updateDatasourceInstance(exploreId, newDataSourceInstance)); - dispatch(loadDatasource(exploreId, newDataSourceInstance)); + + try { + await dispatch(loadDatasource(exploreId, newDataSourceInstance)); + } catch (error) { + console.error(error); + return; + } + + dispatch(runQueries(exploreId)); }; } @@ -197,7 +205,14 @@ export function initializeExplore( } dispatch(updateDatasourceInstance(exploreId, instance)); - dispatch(loadDatasource(exploreId, instance)); + + try { + await dispatch(loadDatasource(exploreId, instance)); + } catch (error) { + console.error(error); + return; + } + dispatch(runQueries(exploreId, true)); } else { dispatch(loadDatasourceMissing(exploreId)); } @@ -343,8 +358,8 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T // Keep ID to track selection dispatch(loadDatasourcePending(exploreId, datasourceName)); - let datasourceError = null; + try { const testResult = await instance.testDatasource(); datasourceError = testResult.status === 'success' ? null : testResult.message; @@ -354,7 +369,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T if (datasourceError) { dispatch(loadDatasourceFailure(exploreId, datasourceError)); - return; + return Promise.reject(`${datasourceName} loading failed`); } if (datasourceName !== getState().explore[exploreId].requestedDatasourceName) { @@ -372,7 +387,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T } dispatch(loadDatasourceSuccess(exploreId, instance)); - dispatch(runQueries(exploreId)); + return Promise.resolve(); }; } @@ -572,14 +587,14 @@ export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult /** * Main action to run queries and dispatches sub-actions based on which result viewers are active */ -export function runQueries(exploreId: ExploreId) { +export function runQueries(exploreId: ExploreId, ignoreUIState = false) { return (dispatch, getState) => { const { datasourceInstance, modifiedQueries, - // showingLogs, - // showingGraph, - // showingTable, + showingLogs, + showingGraph, + showingTable, supportsGraph, supportsLogs, supportsTable, @@ -596,7 +611,7 @@ export function runQueries(exploreId: ExploreId) { const interval = datasourceInstance.interval; // Keep table queries first since they need to return quickly - if (/*showingTable &&*/ supportsTable) { + if ((ignoreUIState || showingTable) && supportsTable) { dispatch( runQueriesForType( exploreId, @@ -611,7 +626,7 @@ export function runQueries(exploreId: ExploreId) { ) ); } - if (/*showingGraph &&*/ supportsGraph) { + if ((ignoreUIState || showingGraph) && supportsGraph) { dispatch( runQueriesForType( exploreId, @@ -625,9 +640,10 @@ export function runQueries(exploreId: ExploreId) { ) ); } - if (/*showingLogs &&*/ supportsLogs) { + if ((ignoreUIState || showingLogs) && supportsLogs) { dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' })); } + dispatch(stateSave()); }; } From 1a0b21b8d1e2dc13037a908e7bbb2deba327acfb Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 1 Feb 2019 15:27:02 +0100 Subject: [PATCH 12/24] Minor post review changes --- public/app/core/utils/explore.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 7128019b1fb..faf46118718 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -157,6 +157,8 @@ const isMetricSegment = (segment: { [key: string]: string }) => segment.hasOwnPr const isUISegment = (segment: { [key: string]: string }) => segment.hasOwnProperty('ui'); export function parseUrlState(initial: string | undefined): ExploreUrlState { + let uiState = DEFAULT_UI_STATE; + if (initial) { try { const parsed = JSON.parse(decodeURI(initial)); @@ -169,8 +171,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { to: parsed[1], }; const datasource = parsed[2]; - let queries = [], - ui; + let queries = []; parsed.slice(3).forEach(segment => { if (isMetricSegment(segment)) { @@ -178,7 +179,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { } if (isUISegment(segment)) { - ui = { + uiState = { showingGraph: segment.ui[0], showingLogs: segment.ui[1], showingTable: segment.ui[2], @@ -186,14 +187,14 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { } }); - return { datasource, queries, range, ui }; + return { datasource, queries, range, ui: uiState }; } return parsed; } catch (e) { console.error(e); } } - return { datasource: null, queries: [], range: DEFAULT_RANGE, ui: DEFAULT_UI_STATE }; + return { datasource: null, queries: [], range: DEFAULT_RANGE, ui: uiState }; } export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: boolean): string { From bd6fed54de73d195df50a7e02666a454e3120726 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 1 Feb 2019 15:45:47 +0100 Subject: [PATCH 13/24] first stuff --- .../ValueMappingsEditor.story.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx new file mode 100644 index 00000000000..31ba6454753 --- /dev/null +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { ValueMappingsEditor } from './ValueMappingsEditor'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; + +const ValueMappingsEditorStories = storiesOf('UI/ValueMappingsEditor', module); + +ValueMappingsEditorStories.addDecorator(withCenteredStory); + +ValueMappingsEditorStories.add('default', () => { + return ( + { + action('Mapping changed'); + }} + /> + ); +}); From 9ac960a80380b6b7612c443c041c0055bbd95759 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Sat, 2 Feb 2019 00:48:13 +0100 Subject: [PATCH 14/24] did not add file, removing centerered --- .../ValueMappingsEditor/ValueMappingsEditor.story.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx index 31ba6454753..d6c8cec8c1e 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx @@ -2,12 +2,9 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { ValueMappingsEditor } from './ValueMappingsEditor'; -import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; const ValueMappingsEditorStories = storiesOf('UI/ValueMappingsEditor', module); -ValueMappingsEditorStories.addDecorator(withCenteredStory); - ValueMappingsEditorStories.add('default', () => { return ( Date: Sun, 3 Feb 2019 16:29:35 -0500 Subject: [PATCH 15/24] Add AWS/Neptune to metricsMap and dimensionsMap --- pkg/tsdb/cloudwatch/metric_find_query.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index dfa03d2dfa9..f898a65f911 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -95,6 +95,7 @@ func init() { "AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"}, "AWS/ML": {"PredictCount", "PredictFailureCount"}, "AWS/NATGateway": {"PacketsOutToDestination", "PacketsOutToSource", "PacketsInFromSource", "PacketsInFromDestination", "BytesOutToDestination", "BytesOutToSource", "BytesInFromSource", "BytesInFromDestination", "ErrorPortAllocation", "ActiveConnectionCount", "ConnectionAttemptCount", "ConnectionEstablishedCount", "IdleTimeoutCount", "PacketsDropCount"}, + "AWS/Neptune": {"CPUUtilization", "ClusterReplicaLag", "ClusterReplicaLagMaximum", "ClusterReplicaLagMinimum", "EngineUptime", "FreeableMemory", "FreeLocalStorage", "GremlinHttp1xx", "GremlinHttp2xx", "GremlinHttp4xx", "GremlinHttp5xx", "GremlinErrors", "GremlinRequests", "GremlinRequestsPerSec", "GremlinWebSocketSuccess", "GremlinWebSocketClientErrors", "GremlinWebSocketServerErrors", "GremlinWebSocketAvailableConnections", "Http1xx", "Http2xx", "Http4xx", "Http5xx", "Http100", "Http101", "Http200", "Http400", "Http403", "Http405", "Http413", "Http429", "Http500", "Http501", "LoaderErrors", "LoaderRequests", "NetworkReceiveThroughput", "NetworkThroughput", "NetworkTransmitThroughput", "SparqlHttp1xx", "SparqlHttp2xx", "SparqlHttp4xx", "SparqlHttp5xx", "SparqlErrors", "SparqlRequests", "SparqlRequestsPerSec", "StatusErrors", "StatusRequests", "VolumeBytesUsed", "VolumeReadIOPs", "VolumeWriteIOPs"}, "AWS/NetworkELB": {"ActiveFlowCount", "ConsumedLCUs", "HealthyHostCount", "NewFlowCount", "ProcessedBytes", "TCP_Client_Reset_Count", "TCP_ELB_Reset_Count", "TCP_Target_Reset_Count", "UnHealthyHostCount"}, "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "QueriesCompletedPerSecond", "QueryDuration", "QueryRuntimeBreakdown", "ReadIOPS", "ReadLatency", "ReadThroughput", "WLMQueriesCompletedPerSecond", "WLMQueryDuration", "WLMQueueLength", "WriteIOPS", "WriteLatency", "WriteThroughput"}, @@ -149,6 +150,7 @@ func init() { "AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"}, "AWS/ML": {"MLModelId", "RequestMode"}, "AWS/NATGateway": {"NatGatewayId"}, + "AWS/Neptune": {"DBClusterIdentifier", "Role", "DatabaseClass", "EngineName"}, "AWS/NetworkELB": {"LoadBalancer", "TargetGroup", "AvailabilityZone"}, "AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"}, "AWS/Redshift": {"NodeID", "ClusterIdentifier", "latency", "service class", "wmlid"}, From f5084045f216c79702ad7124ae72a7144d014881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 4 Feb 2019 09:32:39 +0100 Subject: [PATCH 16/24] Fix save provisioned dashboard modal --- public/app/features/dashboard/components/SaveModals/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/dashboard/components/SaveModals/index.ts b/public/app/features/dashboard/components/SaveModals/index.ts index afab0796d28..6f55cc2ce06 100644 --- a/public/app/features/dashboard/components/SaveModals/index.ts +++ b/public/app/features/dashboard/components/SaveModals/index.ts @@ -1,2 +1,3 @@ export { SaveDashboardAsModalCtrl } from './SaveDashboardAsModalCtrl'; export { SaveDashboardModalCtrl } from './SaveDashboardModalCtrl'; +export { SaveProvisionedDashboardModalCtrl } from './SaveProvisionedDashboardModalCtrl'; From fdd5ac1895e4ee7af5c9a060ba2c8fc8b4ef830d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Feb 2019 09:55:23 +0100 Subject: [PATCH 17/24] devenv: switching back using loki master plus various fixes --- devenv/docker/blocks/loki/docker-compose.yaml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/devenv/docker/blocks/loki/docker-compose.yaml b/devenv/docker/blocks/loki/docker-compose.yaml index c2fee15b0bb..0ac5d439354 100644 --- a/devenv/docker/blocks/loki/docker-compose.yaml +++ b/devenv/docker/blocks/loki/docker-compose.yaml @@ -1,24 +1,14 @@ -version: "3" - -networks: loki: - -services: - loki: - image: grafana/loki:master-3e6a75e + image: grafana/loki:master ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml - networks: - - loki promtail: - image: grafana/promtail:master-3e6a75e + image: grafana/promtail:master volumes: - ./docker/blocks/loki/config.yaml:/etc/promtail/docker-config.yaml - /var/log:/var/log - ../data/log:/var/log/grafana command: -config.file=/etc/promtail/docker-config.yaml - networks: - - loki From c61e90543411fee4971f9f67918ef09de32065ac Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 4 Feb 2019 10:22:45 +0100 Subject: [PATCH 18/24] fixing logging action --- .../ValueMappingsEditor/ValueMappingsEditor.story.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx index d6c8cec8c1e..85504f6cd09 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.story.tsx @@ -6,12 +6,5 @@ import { ValueMappingsEditor } from './ValueMappingsEditor'; const ValueMappingsEditorStories = storiesOf('UI/ValueMappingsEditor', module); ValueMappingsEditorStories.add('default', () => { - return ( - { - action('Mapping changed'); - }} - /> - ); + return ; }); From eb8dfefb231559acce4291a9d288d8d44fc02b50 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Feb 2019 11:16:11 +0100 Subject: [PATCH 19/24] changelog: add notes about closing #14231 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7164f5d99a9..1a19957b3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Minor * **Pushover**: Adds support for images in pushover notifier [#10780](https://github.com/grafana/grafana/issues/10780), thx [@jpenalbae](https://github.com/jpenalbae) +* **Cloudwatch**: Add AWS/Neptune metrics [#14231](https://github.com/grafana/grafana/issues/14231), thx [@tcpatterson](https://github.com/tcpatterson) # 6.0.0-beta1 (2019-01-30) From d433ca7d40d0dfc4a154163334efbe6efeea7cc6 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Feb 2019 13:10:32 +0100 Subject: [PATCH 20/24] fix util for splitting host and port Now you can provide both a default host and a default port --- pkg/services/sqlstore/sqlstore.go | 5 +- pkg/tsdb/mssql/mssql.go | 5 +- pkg/util/ip.go | 25 --------- pkg/util/ip_address.go | 47 +++++++++++++---- pkg/util/ip_address_test.go | 84 ++++++++++++++++++++++++++++++- pkg/util/ip_test.go | 43 ---------------- 6 files changed, 121 insertions(+), 88 deletions(-) delete mode 100644 pkg/util/ip.go delete mode 100644 pkg/util/ip_test.go diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index fb0f0938573..6debaca89a1 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -242,10 +242,7 @@ func (ss *SqlStore) buildConnectionString() (string, error) { cnnstr += ss.buildExtraConnectionString('&') case migrator.POSTGRES: - host, port, err := util.SplitIPPort(ss.dbCfg.Host, "5432") - if err != nil { - return "", err - } + host, port := util.SplitHostPortDefault(ss.dbCfg.Host, "127.0.0.1", "5432") if ss.dbCfg.Pwd == "" { ss.dbCfg.Pwd = "''" } diff --git a/pkg/tsdb/mssql/mssql.go b/pkg/tsdb/mssql/mssql.go index bd4510f6cf3..12f2b6c03c9 100644 --- a/pkg/tsdb/mssql/mssql.go +++ b/pkg/tsdb/mssql/mssql.go @@ -49,10 +49,7 @@ func generateConnectionString(datasource *models.DataSource) (string, error) { } } - server, port, err := util.SplitIPPort(datasource.Url, "1433") - if err != nil { - return "", err - } + server, port := util.SplitHostPortDefault(datasource.Url, "localhost", "1433") encrypt := datasource.JsonData.Get("encrypt").MustString("false") connStr := fmt.Sprintf("server=%s;port=%s;database=%s;user id=%s;password=%s;", diff --git a/pkg/util/ip.go b/pkg/util/ip.go deleted file mode 100644 index d3809318191..00000000000 --- a/pkg/util/ip.go +++ /dev/null @@ -1,25 +0,0 @@ -package util - -import ( - "net" -) - -// SplitIPPort splits the ip string and port. -func SplitIPPort(ipStr string, portDefault string) (ip string, port string, err error) { - ipAddr := net.ParseIP(ipStr) - - if ipAddr == nil { - // Port was included - ip, port, err = net.SplitHostPort(ipStr) - - if err != nil { - return "", "", err - } - } else { - // No port was included - ip = ipAddr.String() - port = portDefault - } - - return ip, port, nil -} diff --git a/pkg/util/ip_address.go b/pkg/util/ip_address.go index d8d95ef3acd..b5ffb361e0b 100644 --- a/pkg/util/ip_address.go +++ b/pkg/util/ip_address.go @@ -7,23 +7,48 @@ import ( // ParseIPAddress parses an IP address and removes port and/or IPV6 format func ParseIPAddress(input string) string { - s := input - lastIndex := strings.LastIndex(input, ":") + host, _ := SplitHostPort(input) - if lastIndex != -1 { - if lastIndex > 0 && input[lastIndex-1:lastIndex] != ":" { - s = input[:lastIndex] - } + ip := net.ParseIP(host) + + if ip == nil { + return host } - s = strings.Replace(s, "[", "", -1) - s = strings.Replace(s, "]", "", -1) - - ip := net.ParseIP(s) - if ip.IsLoopback() { return "127.0.0.1" } return ip.String() } + +// SplitHostPortDefault splits ip address/hostname string by host and port. Defaults used if no match found +func SplitHostPortDefault(input, defaultHost, defaultPort string) (host string, port string) { + port = defaultPort + s := input + lastIndex := strings.LastIndex(input, ":") + + if lastIndex != -1 { + if lastIndex > 0 && input[lastIndex-1:lastIndex] != ":" { + s = input[:lastIndex] + port = input[lastIndex+1:] + } else if lastIndex == 0 { + s = defaultHost + port = input[lastIndex+1:] + } + } else { + port = defaultPort + } + + s = strings.Replace(s, "[", "", -1) + s = strings.Replace(s, "]", "", -1) + port = strings.Replace(port, "[", "", -1) + port = strings.Replace(port, "]", "", -1) + + return s, port +} + +// SplitHostPort splits ip address/hostname string by host and port +func SplitHostPort(input string) (host string, port string) { + return SplitHostPortDefault(input, "", "") +} diff --git a/pkg/util/ip_address_test.go b/pkg/util/ip_address_test.go index fd3e3ea8587..b926de1a36b 100644 --- a/pkg/util/ip_address_test.go +++ b/pkg/util/ip_address_test.go @@ -9,8 +9,90 @@ import ( func TestParseIPAddress(t *testing.T) { Convey("Test parse ip address", t, func() { So(ParseIPAddress("192.168.0.140:456"), ShouldEqual, "192.168.0.140") + So(ParseIPAddress("192.168.0.140"), ShouldEqual, "192.168.0.140") So(ParseIPAddress("[::1:456]"), ShouldEqual, "127.0.0.1") So(ParseIPAddress("[::1]"), ShouldEqual, "127.0.0.1") - So(ParseIPAddress("192.168.0.140"), ShouldEqual, "192.168.0.140") + So(ParseIPAddress("::1"), ShouldEqual, "127.0.0.1") + So(ParseIPAddress("::1:123"), ShouldEqual, "127.0.0.1") + }) +} + +func TestSplitHostPortDefault(t *testing.T) { + Convey("Test split ip address to host and port", t, func() { + host, port := SplitHostPortDefault("192.168.0.140:456", "", "") + So(host, ShouldEqual, "192.168.0.140") + So(port, ShouldEqual, "456") + + host, port = SplitHostPortDefault("192.168.0.140", "", "123") + So(host, ShouldEqual, "192.168.0.140") + So(port, ShouldEqual, "123") + + host, port = SplitHostPortDefault("[::1:456]", "", "") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "456") + + host, port = SplitHostPortDefault("[::1]", "", "123") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "123") + + host, port = SplitHostPortDefault("::1:123", "", "") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "123") + + host, port = SplitHostPortDefault("::1", "", "123") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "123") + + host, port = SplitHostPortDefault(":456", "1.2.3.4", "") + So(host, ShouldEqual, "1.2.3.4") + So(port, ShouldEqual, "456") + + host, port = SplitHostPortDefault("xyz.rds.amazonaws.com", "", "123") + So(host, ShouldEqual, "xyz.rds.amazonaws.com") + So(port, ShouldEqual, "123") + + host, port = SplitHostPortDefault("xyz.rds.amazonaws.com:123", "", "") + So(host, ShouldEqual, "xyz.rds.amazonaws.com") + So(port, ShouldEqual, "123") + }) +} + +func TestSplitHostPort(t *testing.T) { + Convey("Test split ip address to host and port", t, func() { + host, port := SplitHostPort("192.168.0.140:456") + So(host, ShouldEqual, "192.168.0.140") + So(port, ShouldEqual, "456") + + host, port = SplitHostPort("192.168.0.140") + So(host, ShouldEqual, "192.168.0.140") + So(port, ShouldEqual, "") + + host, port = SplitHostPort("[::1:456]") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "456") + + host, port = SplitHostPort("[::1]") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "") + + host, port = SplitHostPort("::1:123") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "123") + + host, port = SplitHostPort("::1") + So(host, ShouldEqual, "::1") + So(port, ShouldEqual, "") + + host, port = SplitHostPort(":456") + So(host, ShouldEqual, "") + So(port, ShouldEqual, "456") + + host, port = SplitHostPort("xyz.rds.amazonaws.com") + So(host, ShouldEqual, "xyz.rds.amazonaws.com") + So(port, ShouldEqual, "") + + host, port = SplitHostPort("xyz.rds.amazonaws.com:123") + So(host, ShouldEqual, "xyz.rds.amazonaws.com") + So(port, ShouldEqual, "123") }) } diff --git a/pkg/util/ip_test.go b/pkg/util/ip_test.go deleted file mode 100644 index 3a62a080e26..00000000000 --- a/pkg/util/ip_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package util - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func TestSplitIPPort(t *testing.T) { - - Convey("When parsing an IPv4 without explicit port", t, func() { - ip, port, err := SplitIPPort("1.2.3.4", "5678") - - So(err, ShouldEqual, nil) - So(ip, ShouldEqual, "1.2.3.4") - So(port, ShouldEqual, "5678") - }) - - Convey("When parsing an IPv6 without explicit port", t, func() { - ip, port, err := SplitIPPort("::1", "5678") - - So(err, ShouldEqual, nil) - So(ip, ShouldEqual, "::1") - So(port, ShouldEqual, "5678") - }) - - Convey("When parsing an IPv4 with explicit port", t, func() { - ip, port, err := SplitIPPort("1.2.3.4:56", "78") - - So(err, ShouldEqual, nil) - So(ip, ShouldEqual, "1.2.3.4") - So(port, ShouldEqual, "56") - }) - - Convey("When parsing an IPv6 with explicit port", t, func() { - ip, port, err := SplitIPPort("[::1]:56", "78") - - So(err, ShouldEqual, nil) - So(ip, ShouldEqual, "::1") - So(port, ShouldEqual, "56") - }) - -} From bc21c9520fafee7c6a0380389d695526aa921fd8 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 4 Feb 2019 15:07:11 +0100 Subject: [PATCH 21/24] fix: Data source picker in panel queries options should overlap content below, including ace scrollbar #15122 --- public/sass/components/_toolbar.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/public/sass/components/_toolbar.scss b/public/sass/components/_toolbar.scss index 14db85f7e65..36be8a18739 100644 --- a/public/sass/components/_toolbar.scss +++ b/public/sass/components/_toolbar.scss @@ -4,7 +4,6 @@ align-items: center; padding: 3px 20px 3px 20px; position: relative; - z-index: 1; flex: 0 0 auto; background: $toolbar-bg; border-radius: 3px; From 648bec1807b9685693b8664ed730d5f5b9975434 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 4 Feb 2019 15:19:23 +0100 Subject: [PATCH 22/24] fix: Set ace editor min height to avoid problem with scrollbar overlapping ace content #15122 --- public/sass/components/_code_editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_code_editor.scss b/public/sass/components/_code_editor.scss index 4f50495789d..a9c7ebf2e75 100644 --- a/public/sass/components/_code_editor.scss +++ b/public/sass/components/_code_editor.scss @@ -7,7 +7,7 @@ &.ace_editor { @include font-family-monospace(); font-size: 1rem; - min-height: 2.6rem; + min-height: 3.6rem; // Include space for horizontal scrollbar @include border-radius($input-border-radius-sm); border: $input-btn-border-width solid $input-border-color; From 9ab5eeb7f30cdbcd1b950ecbaeed8938679650b5 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 4 Feb 2019 17:26:20 +0100 Subject: [PATCH 23/24] fix: Explore: Query wrapping on long queries #15222 --- .../src/components/Select/SelectOptionGroup.tsx | 2 +- .../app/features/dashboard/panel_editor/QueriesTab.tsx | 2 +- public/app/features/explore/QueryRow.tsx | 4 ++-- .../prometheus/components/PromQueryField.tsx | 6 +++--- public/sass/components/_gf-form.scss | 4 ++++ public/sass/utils/_utils.scss | 10 +++++++++- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx b/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx index efc5e4516fc..9a787a84819 100644 --- a/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx +++ b/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx @@ -49,7 +49,7 @@ export default class SelectOptionGroup extends PureComponent
- {label} + {label} {' '}
{expanded && children} diff --git a/public/app/features/dashboard/panel_editor/QueriesTab.tsx b/public/app/features/dashboard/panel_editor/QueriesTab.tsx index 491f255d761..d46ff020906 100644 --- a/public/app/features/dashboard/panel_editor/QueriesTab.tsx +++ b/public/app/features/dashboard/panel_editor/QueriesTab.tsx @@ -133,7 +133,7 @@ export class QueriesTab extends PureComponent { return ( <> -
+
{!isAddingMixed && (
-
+
Date: Mon, 4 Feb 2019 17:37:07 +0100 Subject: [PATCH 24/24] now /api/login/ping returns Response --- pkg/api/api.go | 4 ++-- pkg/api/login.go | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 07cb712f794..980706d8355 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -108,8 +108,8 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/api/snapshots-delete/:deleteKey", Wrap(DeleteDashboardSnapshotByDeleteKey)) r.Delete("/api/snapshots/:key", reqEditorRole, Wrap(DeleteDashboardSnapshot)) - // api renew session based on remember cookie - r.Get("/api/login/ping", quota("session"), hs.LoginAPIPing) + // api renew session based on cookie + r.Get("/api/login/ping", quota("session"), Wrap(hs.LoginAPIPing)) // authed api r.Group("/api", func(apiRoute routing.RouteRegister) { diff --git a/pkg/api/login.go b/pkg/api/login.go index 3f2d82a6c0f..50c62e0835a 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -78,13 +78,12 @@ func tryOAuthAutoLogin(c *m.ReqContext) bool { return false } -func (hs *HTTPServer) LoginAPIPing(c *m.ReqContext) { - if c.IsSignedIn || (c.AllowAnonymous && c.IsAnonymous) { - c.JsonOK("Logged in") - return +func (hs *HTTPServer) LoginAPIPing(c *m.ReqContext) Response { + if c.IsSignedIn || c.IsAnonymous { + return JSON(200, "Logged in") } - c.JsonApiErr(401, "Unauthorized", nil) + return Error(401, "Unauthorized", nil) } func (hs *HTTPServer) LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response {