Kaynağa Gözat

Merge branch 'master' into docs-5.1

Marcus Efraimsson 7 yıl önce
ebeveyn
işleme
1d515bb52c
100 değiştirilmiş dosya ile 6183 ekleme ve 729 silme
  1. 8 8
      .github/ISSUE_TEMPLATE.md
  2. 38 3
      CHANGELOG.md
  3. 17 5
      Gopkg.lock
  4. 6 4
      Gopkg.toml
  5. 8 2
      Makefile
  6. 2 5
      README.md
  7. 6 4
      ROADMAP.md
  8. 8 0
      build.go
  9. 132 54
      circle.yml
  10. 6 0
      conf/defaults.ini
  11. 6 6
      conf/sample.ini
  12. 7 0
      docker/blocks/elastic/docker-compose.yaml
  13. 7 0
      docker/blocks/elastic5/docker-compose.yaml
  14. 1161 0
      docker/blocks/graphite1/big-dashboard.json
  15. 1179 0
      docker/blocks/graphite11/big-dashboard.json
  16. 18 0
      docker/blocks/graphite11/docker-compose.yaml
  17. 5 0
      docker/blocks/mssql/build/Dockerfile
  18. 2 0
      docker/blocks/mssql/build/entrypoint.sh
  19. 12 0
      docker/blocks/mssql/build/setup.sh
  20. 14 0
      docker/blocks/mssql/build/setup.sql.template
  21. 539 0
      docker/blocks/mssql/dashboard.json
  22. 19 0
      docker/blocks/mssql/docker-compose.yaml
  23. 2242 0
      docker/blocks/mssql_tests/dashboard.json
  24. 12 0
      docker/blocks/mssql_tests/docker-compose.yaml
  25. 6 0
      docker/blocks/prometheus/docker-compose.yaml
  26. 6 2
      docker/blocks/prometheus/prometheus.yml
  27. 1 1
      docker/blocks/prometheus2/Dockerfile
  28. 6 0
      docker/blocks/prometheus2/docker-compose.yaml
  29. 6 2
      docker/blocks/prometheus2/prometheus.yml
  30. 18 0
      docker/blocks/prometheus_random_data/Dockerfile
  31. 9 3
      docs/sources/administration/provisioning.md
  32. 2 0
      docs/sources/alerting/notifications.md
  33. 1 1
      docs/sources/alerting/rules.md
  34. 2 1
      docs/sources/features/datasources/cloudwatch.md
  35. 1 1
      docs/sources/features/datasources/prometheus.md
  36. 5 22
      docs/sources/http_api/dashboard.md
  37. 1 0
      docs/sources/http_api/index.md
  38. 5 0
      docs/sources/installation/configuration.md
  39. 3 3
      docs/sources/installation/debian.md
  40. 1 1
      docs/sources/installation/docker.md
  41. 5 5
      docs/sources/installation/rpm.md
  42. 1 1
      docs/sources/installation/windows.md
  43. 68 1
      docs/sources/reference/templating.md
  44. 22 14
      docs/sources/tutorials/ha_setup.md
  45. 1 1
      emails/templates/layouts/default.html
  46. 2 2
      package.json
  47. 1 1
      packaging/publish/publish_both.sh
  48. 11 3
      pkg/api/admin.go
  49. 11 12
      pkg/api/admin_users.go
  50. 33 34
      pkg/api/alerting.go
  51. 1 2
      pkg/api/alerting_test.go
  52. 25 26
      pkg/api/annotations.go
  53. 2 3
      pkg/api/annotations_test.go
  54. 22 21
      pkg/api/api.go
  55. 3 4
      pkg/api/apikey.go
  56. 3 3
      pkg/api/app_routes.go
  57. 4 4
      pkg/api/common.go
  58. 8 8
      pkg/api/common_test.go
  59. 29 32
      pkg/api/dashboard.go
  60. 10 11
      pkg/api/dashboard_permission.go
  61. 1 2
      pkg/api/dashboard_permission_test.go
  62. 7 8
      pkg/api/dashboard_snapshot.go
  63. 12 13
      pkg/api/dashboard_test.go
  64. 5 6
      pkg/api/dataproxy.go
  65. 17 19
      pkg/api/datasources.go
  66. 6 7
      pkg/api/folder.go
  67. 2 3
      pkg/api/folder_permission.go
  68. 1 2
      pkg/api/folder_permission_test.go
  69. 18 21
      pkg/api/folder_test.go
  70. 2 3
      pkg/api/frontendsettings.go
  71. 2 2
      pkg/api/grafana_com_proxy.go
  72. 2 2
      pkg/api/http_server.go
  73. 19 19
      pkg/api/index.go
  74. 12 12
      pkg/api/login.go
  75. 7 6
      pkg/api/login_oauth.go
  76. 10 11
      pkg/api/metrics.go
  77. 14 15
      pkg/api/org.go
  78. 21 23
      pkg/api/org_invite.go
  79. 17 18
      pkg/api/org_users.go
  80. 2 3
      pkg/api/password.go
  81. 10 11
      pkg/api/playlist.go
  82. 27 29
      pkg/api/playlist_play.go
  83. 11 6
      pkg/api/pluginproxy/ds_proxy.go
  84. 5 6
      pkg/api/pluginproxy/ds_proxy_test.go
  85. 1 2
      pkg/api/pluginproxy/pluginproxy.go
  86. 48 47
      pkg/api/plugins.go
  87. 10 11
      pkg/api/preferences.go
  88. 4 5
      pkg/api/quota.go
  89. 2 2
      pkg/api/render.go
  90. 12 13
      pkg/api/search.go
  91. 3 4
      pkg/api/signup.go
  92. 2 3
      pkg/api/stars.go
  93. 5 6
      pkg/api/team.go
  94. 3 4
      pkg/api/team_members.go
  95. 33 34
      pkg/api/user.go
  96. 1 1
      pkg/cmd/grafana-server/server.go
  97. 3 1
      pkg/components/renderer/renderer.go
  98. 10 9
      pkg/middleware/auth.go
  99. 10 9
      pkg/middleware/auth_proxy.go
  100. 17 15
      pkg/middleware/auth_proxy_test.go

+ 8 - 8
.github/ISSUE_TEMPLATE.md

@@ -5,12 +5,12 @@ Read before posting:
 - Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50
 - Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50
 
 
 Please include this information:
 Please include this information:
-- What Grafana version are you using?
-- What datasource are you using?
-- What OS are you running grafana on?
-- What did you do?
-- What was the expected result?
-- What happened instead?
-- If related to metric query / data viz:
-  - Include raw network request & response: get by opening Chrome Dev Tools (F12, Ctrl+Shift+I on windows, Cmd+Opt+I on Mac), go the network tab.
+### What Grafana version are you using?
+### What datasource are you using?
+### What OS are you running grafana on?
+### What did you do?
+### What was the expected result?
+### What happened instead?
+### If related to metric query / data viz:
+### Include raw network request & response: get by opening Chrome Dev Tools (F12, Ctrl+Shift+I on windows, Cmd+Opt+I on Mac), go the network tab.
  
  

+ 38 - 3
CHANGELOG.md

@@ -1,11 +1,46 @@
 # 5.1.0 (unreleased)
 # 5.1.0 (unreleased)
 
 
+* **MSSQL**: New Microsoft SQL Server data source [#10093](https://github.com/grafana/grafana/pull/10093), [#11298](https://github.com/grafana/grafana/pull/11298), thx [@linuxchips](https://github.com/linuxchips)
+* **Prometheus**: The heatmap panel now support Prometheus histograms [#10009](https://github.com/grafana/grafana/issues/10009)
 * **Postgres/MySQL**: Ability to insert 0s or nulls for missing intervals [#9487](https://github.com/grafana/grafana/issues/9487), thanks [@svenklemm](https://github.com/svenklemm)
 * **Postgres/MySQL**: Ability to insert 0s or nulls for missing intervals [#9487](https://github.com/grafana/grafana/issues/9487), thanks [@svenklemm](https://github.com/svenklemm)
-
-
-# 5.0.1 (unreleased)
+* **Graph**: Thresholds for Right Y axis [#7107](https://github.com/grafana/grafana/issues/7107), thx [@ilgizar](https://github.com/ilgizar)
+* **Graph**: Support multiple series stacking in histogram mode [#8151](https://github.com/grafana/grafana/issues/8151), thx [@mtanda](https://github.com/mtanda)
+* **Alerting**: Pausing/un alerts now updates new_state_date [#10942](https://github.com/grafana/grafana/pull/10942)
+* **Alerting**: Support Pagerduty notification channel using Pagerduty V2 API [#10531](https://github.com/grafana/grafana/issues/10531), thx [@jbaublitz](https://github.com/jbaublitz)
+* **Templating**: Add comma templating format [#10632](https://github.com/grafana/grafana/issues/10632), thx [@mtanda](https://github.com/mtanda)
+* **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda)
+
+### Minor
+* **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)
+* **Cloudwatch**: Support high resolution metrics [#10925](https://github.com/grafana/grafana/pull/10925), thx [@mtanda](https://github.com/mtanda)
+* **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw)
+* **Units**: Second to HH:mm:ss formatter [#11107](https://github.com/grafana/grafana/issues/11107), thx [@gladdiologist](https://github.com/gladdiologist)
+* **Singlestat**: Add color to prefix and postfix in singlestat panel [#11143](https://github.com/grafana/grafana/pull/11143), thx [@ApsOps](https://github.com/ApsOps)
+
+# 5.0.4 (unreleased)
+* **Dashboard** Fixed bug where collapsed panels could not be directly linked to/renderer [#11114](https://github.com/grafana/grafana/issues/11114) & [#11086](https://github.com/grafana/grafana/issues/11086)
+
+# 5.0.3 (2018-03-16)
+* **Mysql**: Mysql panic occurring occasionally upon Grafana dashboard access (a bigger patch than the one in 5.0.2) [#11155](https://github.com/grafana/grafana/issues/11155)
+
+# 5.0.2 (2018-03-14)
+* **Mysql**: Mysql panic occurring occasionally upon Grafana dashboard access [#11155](https://github.com/grafana/grafana/issues/11155)
+* **Dashboards**: Should be possible to browse dashboard using only uid [#11231](https://github.com/grafana/grafana/issues/11231)
+* **Alerting**: Fixes bug where alerts from hidden panels where deleted [#11222](https://github.com/grafana/grafana/issues/11222)
+* **Import**: Fixes bug where dashboards with alerts couldn't be imported [#11227](https://github.com/grafana/grafana/issues/11227)
+* **Teams**: Remove quota restrictions from teams [#11220](https://github.com/grafana/grafana/issues/11220)
+* **Render**: Fixes bug with legacy url redirection for panel rendering [#11180](https://github.com/grafana/grafana/issues/11180)
+
+# 5.0.1 (2018-03-08)
 
 
 * **Postgres**: PostgreSQL error when using ipv6 address as hostname in connection string [#11055](https://github.com/grafana/grafana/issues/11055), thanks [@svenklemm](https://github.com/svenklemm)
 * **Postgres**: PostgreSQL error when using ipv6 address as hostname in connection string [#11055](https://github.com/grafana/grafana/issues/11055), thanks [@svenklemm](https://github.com/svenklemm)
+* **Dashboards**: Changing templated value from dropdown is causing unsaved changes [#11063](https://github.com/grafana/grafana/issues/11063)
+* **Prometheus**: Fixes bundled Prometheus 2.0 dashboard [#11016](https://github.com/grafana/grafana/issues/11016), thx [@roidelapluie](https://github.com/roidelapluie)
+* **Sidemenu**: Profile menu "invisible" when gravatar is disabled [#11097](https://github.com/grafana/grafana/issues/11097)
+* **Dashboard**: Fixes a bug with resizeable handles for panels [#11103](https://github.com/grafana/grafana/issues/11103)
+* **Alerting**: Telegram inline image mode fails when caption too long [#10975](https://github.com/grafana/grafana/issues/10975)
+* **Alerting**: Fixes silent failing validation [#11145](https://github.com/grafana/grafana/pull/11145)
+* **OAuth**: Only use jwt token if it contains an email address [#11127](https://github.com/grafana/grafana/pull/11127)
 
 
 # 5.0.0-stable (2018-03-01)
 # 5.0.0-stable (2018-03-01)
 
 

+ 17 - 5
Gopkg.lock

@@ -103,6 +103,14 @@
   revision = "346938d642f2ec3594ed81d874461961cd0faa76"
   revision = "346938d642f2ec3594ed81d874461961cd0faa76"
   version = "v1.1.0"
   version = "v1.1.0"
 
 
+[[projects]]
+  name = "github.com/denisenkom/go-mssqldb"
+  packages = [
+    ".",
+    "internal/cp"
+  ]
+  revision = "270bc3860bb94dd3a3ffd047377d746c5e276726"
+
 [[projects]]
 [[projects]]
   name = "github.com/fatih/color"
   name = "github.com/fatih/color"
   packages = ["."]
   packages = ["."]
@@ -145,7 +153,6 @@
   packages = [
   packages = [
     ".",
     ".",
     "memcache",
     "memcache",
-    "mysql",
     "postgres",
     "postgres",
     "redis"
     "redis"
   ]
   ]
@@ -171,12 +178,14 @@
 [[projects]]
 [[projects]]
   name = "github.com/go-xorm/core"
   name = "github.com/go-xorm/core"
   packages = ["."]
   packages = ["."]
-  revision = "e8409d73255791843585964791443dbad877058c"
+  revision = "da1adaf7a28ca792961721a34e6e04945200c890"
+  version = "v0.5.7"
 
 
 [[projects]]
 [[projects]]
   name = "github.com/go-xorm/xorm"
   name = "github.com/go-xorm/xorm"
   packages = ["."]
   packages = ["."]
-  revision = "6687a2b4e824f4d87f2d65060ec5cb0d896dff1e"
+  revision = "1933dd69e294c0a26c0266637067f24dbb25770c"
+  version = "v0.6.4"
 
 
 [[projects]]
 [[projects]]
   branch = "master"
   branch = "master"
@@ -462,7 +471,10 @@
 [[projects]]
 [[projects]]
   branch = "master"
   branch = "master"
   name = "golang.org/x/crypto"
   name = "golang.org/x/crypto"
-  packages = ["pbkdf2"]
+  packages = [
+    "md4",
+    "pbkdf2"
+  ]
   revision = "3d37316aaa6bd9929127ac9a527abf408178ea7b"
   revision = "3d37316aaa6bd9929127ac9a527abf408178ea7b"
 
 
 [[projects]]
 [[projects]]
@@ -631,6 +643,6 @@
 [solve-meta]
 [solve-meta]
   analyzer-name = "dep"
   analyzer-name = "dep"
   analyzer-version = 1
   analyzer-version = 1
-  inputs-digest = "4de68f1342ba98a637ec8ca7496aeeae2021bf9e4c7c80db7924e14709151a62"
+  inputs-digest = "8a9e651fb8ea49dfd3c6ddc99bd3242b39e453ea9edd11321da79bd2c865e9d1"
   solver-name = "gps-cdcl"
   solver-name = "gps-cdcl"
   solver-version = 1
   solver-version = 1

+ 6 - 4
Gopkg.toml

@@ -85,13 +85,11 @@ ignored = [
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/go-xorm/core"
   name = "github.com/go-xorm/core"
-  revision = "e8409d73255791843585964791443dbad877058c"
-  #version = "0.5.7" //keeping this since we would rather depend on version then commit
+  version = "0.5.7"
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/go-xorm/xorm"
   name = "github.com/go-xorm/xorm"
-  revision = "6687a2b4e824f4d87f2d65060ec5cb0d896dff1e"
-  #version = "0.6.4" //keeping this since we would rather depend on version then commit
+  version = "0.6.4"
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/gorilla/websocket"
   name = "github.com/gorilla/websocket"
@@ -197,3 +195,7 @@ ignored = [
 [[constraint]]
 [[constraint]]
   branch = "master"
   branch = "master"
   name = "github.com/teris-io/shortid"
   name = "github.com/teris-io/shortid"
+
+[[constraint]]
+  name = "github.com/denisenkom/go-mssqldb"
+  revision = "270bc3860bb94dd3a3ffd047377d746c5e276726"

+ 8 - 2
Makefile

@@ -11,8 +11,14 @@ deps: deps-js
 build-go:
 build-go:
 	go run build.go build
 	go run build.go build
 
 
+build-server:
+	go run build.go build-server
+
+build-cli:
+	go run build.go build-cli
+
 build-js:
 build-js:
-	npm run build
+	yarn run build
 
 
 build: build-go build-js
 build: build-go build-js
 
 
@@ -20,7 +26,7 @@ test-go:
 	go test -v ./pkg/...
 	go test -v ./pkg/...
 
 
 test-js:
 test-js:
-	npm test
+	yarn test
 
 
 test: test-go test-js
 test: test-go test-js
 
 

+ 2 - 5
README.md

@@ -9,9 +9,6 @@ Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB.
 
 
 ![](http://docs.grafana.org/assets/img/features/dashboard_ex1.png)
 ![](http://docs.grafana.org/assets/img/features/dashboard_ex1.png)
 
 
-## Grafana v5 Alpha Preview
-Grafana master is now v5.0 alpha. This is going to be the biggest and most foundational release Grafana has ever had, coming with a ton of UX improvements, a new dashboard grid engine, dashboard folders, user teams and permissions. Checkout out this [video preview](https://www.youtube.com/watch?v=BC_YRNpqj5k) of Grafana v5.
-
 ## Installation
 ## Installation
 Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get)
 Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get)
 the latest release.
 the latest release.
@@ -27,13 +24,13 @@ the latest master builds [here](https://grafana.com/grafana/download)
 
 
 ### Dependencies
 ### Dependencies
 
 
-- Go 1.9
+- Go 1.10
 - NodeJS LTS
 - NodeJS LTS
 
 
 ### Building the backend
 ### Building the backend
 ```bash
 ```bash
 go get github.com/grafana/grafana
 go get github.com/grafana/grafana
-cd ~/go/src/github.com/grafana/grafana
+cd $GOPATH/src/github.com/grafana/grafana
 go run build.go setup
 go run build.go setup
 go run build.go build
 go run build.go build
 ```
 ```

+ 6 - 4
ROADMAP.md

@@ -6,19 +6,21 @@ But it will give you an idea of our current vision and plan.
 ### Short term (1-2 months)
 ### Short term (1-2 months)
 
 
 - v5.1
 - v5.1
-  - Crossplatform builds & build speed improvements
+  - Build speed improvements & integration test execution
+  - Kubernetes friendly docker container
   - Enterprise LDAP
   - Enterprise LDAP
-  - New template interpolation syntax
   - Provisioning workflow
   - Provisioning workflow
-  - First login registration view
-  - IFQL Initial support
+  - MSSQL datasource
   
   
 ### Mid term (2-4 months)
 ### Mid term (2-4 months)
 
 
 - v5.2
 - v5.2
   - Azure monitor backend rewrite
   - Azure monitor backend rewrite
   - Elasticsearch alerting
   - Elasticsearch alerting
+  - First login registration view
   - Backend plugins? (alert notifiers, auth)
   - Backend plugins? (alert notifiers, auth)
+  - Crossplatform builds
+  - IFQL Initial support
   
   
 ### Long term (4 - 8 months)
 ### Long term (4 - 8 months)
 
 

+ 8 - 0
build.go

@@ -79,10 +79,18 @@ func main() {
 		case "setup":
 		case "setup":
 			setup()
 			setup()
 
 
+		case "build-srv":
+			clean()
+			build("grafana-server", "./pkg/cmd/grafana-server", []string{})
+
 		case "build-cli":
 		case "build-cli":
 			clean()
 			clean()
 			build("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
 			build("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
 
 
+		case "build-server":
+			clean()
+			build("grafana-server", "./pkg/cmd/grafana-server", []string{})
+
 		case "build":
 		case "build":
 			clean()
 			clean()
 			for _, binary := range binaries {
 			for _, binary := range binaries {

+ 132 - 54
circle.yml

@@ -1,57 +1,135 @@
-machine:
-  node:
-    version: 6.11.4
-  python:
-    version: 2.7.3
-  services:
-    - docker
-  environment:
-    GOPATH: "/home/ubuntu/.go_workspace"
-    ORG_PATH: "github.com/grafana"
-    REPO_PATH: "${ORG_PATH}/grafana"
-    GODIST: "go1.9.3.linux-amd64.tar.gz"
-  post:
-    - mkdir -p ~/download
-    - mkdir -p ~/docker
-    - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST
-    - sudo rm -rf /usr/local/go
-    - sudo tar -C /usr/local -xzf download/$GODIST
+version: 2
 
 
-dependencies:
-  cache_directories:
-    - "~/docker"
-    - "~/download"
-  override:
-    - rm -rf ${GOPATH}/src/${REPO_PATH}
-    - mkdir -p ${GOPATH}/src/${ORG_PATH}
-    - cp -r ~/grafana ${GOPATH}/src/${ORG_PATH}
-  pre:
-    - pip install awscli
-    - sudo apt-get update; sudo apt-get install rpm; sudo apt-get install expect
-    - ./scripts/build/build_container.sh
+jobs:
+  test-frontend:
+    docker:
+      - image: circleci/node:6.11.4
+    steps:
+      - checkout
+      - run:
+          name: install yarn
+          command: 'sudo npm install -g yarn --quiet'
+      - restore_cache:
+          key: dependency-cache-{{ checksum "yarn.lock" }}
+      # Could we skip this step if the cache has been restored? `[ -d node_modules ] || yarn install ...` should be able to apply to build step as well
+      - run:
+          name: yarn install
+          command: 'yarn install --pure-lockfile --no-progress'
+      - save_cache:
+          key: dependency-cache-{{ checksum "yarn.lock" }}
+          paths:
+            - node_modules
+      - run:
+          name: frontend tests
+          command: './scripts/circle-test-frontend.sh'
 
 
-test:
-  override:
-    - bash scripts/circle-test-frontend.sh
-    - bash scripts/circle-test-backend.sh
+  test-backend:
+    docker:
+      - image: circleci/golang:1.10
+    working_directory: /go/src/github.com/grafana/grafana
+    steps:
+      - checkout
+      - run:
+          name: build backend and run go tests
+          command: './scripts/circle-test-backend.sh'
 
 
-deployment:
-  gh_branch:
-    branch: master
-    commands:
-      - ./scripts/build/deploy.sh
-      - ./scripts/build/sign_packages.sh
-      - go run build.go sha-dist
-      - aws s3 sync ./dist s3://$BUCKET_NAME/master
-      - ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} master
-      - ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
-      - go run ./scripts/build/publish.go -apiKey ${GRAFANA_COM_API_KEY}
-  gh_tag:
-    tag: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
-    commands:
-      - ./scripts/build/deploy.sh
-      - ./scripts/build/sign_packages.sh
-      - go run build.go sha-dist
-      - aws s3 sync ./dist s3://$BUCKET_NAME/release
-      - ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release
-      - ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} ${CIRCLE_TAG}
+  build:
+    docker:
+     - image: grafana/build-container:v0.1
+    working_directory: /go/src/github.com/grafana/grafana
+    steps:
+      - checkout
+      - run:
+          name: build and package grafana
+          command: './scripts/build/build.sh'
+      - run:
+          name: sign packages
+          command: './scripts/build/sign_packages.sh'
+      - run:
+          name: sha-sum packages
+          command: 'go run build.go sha-dist'
+      - run:
+          name: Build Grafana.com publisher
+          command: 'go build -o scripts/publish scripts/build/publish.go'
+      - persist_to_workspace:
+          root: .
+          paths:
+            - dist/grafana*
+            - scripts/*.sh
+            - scripts/publish
+
+  deploy-master:
+    docker:
+      - image: circleci/python:2.7-stretch
+    steps:
+      - attach_workspace:
+          at: .
+      - run:
+          name: install awscli
+          command: 'sudo pip install awscli'
+      - run:
+          name: deploy to s3
+          command: 'aws s3 sync ./dist s3://$BUCKET_NAME/master'
+      - run:
+          name: Trigger Windows build
+          command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} master'
+      - run:
+          name: Trigger Docker build
+          command: './scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}'
+      - run:
+          name: Publish to Grafana.com
+          command: './scripts/publish -apiKey ${GRAFANA_COM_API_KEY}'
+
+  deploy-release:
+    docker:
+      - image: circleci/python:2.7-stretch
+    steps:
+      - attach_workspace:
+          at: dist
+      - run:
+          name: install awscli
+          command: 'sudo pip install awscli'
+      - run:
+          name: deploy to s3
+          command: 'aws s3 sync ./dist s3://$BUCKET_NAME/release'
+      - run:
+          name: Trigger Windows build
+          command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release'
+      - run:
+          name: Trigger Docker build
+          command: './scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} ${CIRCLE_TAG}'
+
+workflows:
+  version: 2
+  test-and-build:
+    jobs:
+      - build:
+          filters:
+            tags:
+              only: /.*/
+      - test-frontend:
+          filters:
+            tags:
+              only: /.*/
+      - test-backend:
+          filters:
+            tags:
+              only: /.*/
+      - deploy-master:
+          requires:
+            - test-backend
+            - test-frontend
+            - build
+          filters:
+            branches:
+              only: master
+      - deploy-release:
+          requires:
+            - test-backend
+            - test-frontend
+            - build
+          filters:
+            branches:
+              ignore: /.*/
+            tags:
+              only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/

+ 6 - 0
conf/defaults.ini

@@ -82,6 +82,9 @@ max_idle_conn = 2
 # Max conn setting default is 0 (mean not set)
 # Max conn setting default is 0 (mean not set)
 max_open_conn =
 max_open_conn =
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+conn_max_lifetime = 14400
+
 # Set to true to log the sql calls and execution times.
 # Set to true to log the sql calls and execution times.
 log_queries =
 log_queries =
 
 
@@ -125,6 +128,9 @@ cookie_secure = false
 session_life_time = 86400
 session_life_time = 86400
 gc_interval_time = 86400
 gc_interval_time = 86400
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+conn_max_lifetime = 14400
+
 #################################### Data proxy ###########################
 #################################### Data proxy ###########################
 [dataproxy]
 [dataproxy]
 
 

+ 6 - 6
conf/sample.ini

@@ -4,10 +4,10 @@
 # change
 # change
 
 
 # possible values : production, development
 # possible values : production, development
-; app_mode = production
+;app_mode = production
 
 
 # instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
 # instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
-; instance_name = ${HOSTNAME}
+;instance_name = ${HOSTNAME}
 
 
 #################################### Paths ####################################
 #################################### Paths ####################################
 [paths]
 [paths]
@@ -21,7 +21,7 @@
 ;plugins = /var/lib/grafana/plugins
 ;plugins = /var/lib/grafana/plugins
 
 
 # folder that contains provisioning config files that grafana will apply on startup and while running.
 # folder that contains provisioning config files that grafana will apply on startup and while running.
-; provisioning = conf/provisioning
+;provisioning = conf/provisioning
 
 
 #################################### Server ####################################
 #################################### Server ####################################
 [server]
 [server]
@@ -90,6 +90,9 @@
 # Max conn setting default is 0 (mean not set)
 # Max conn setting default is 0 (mean not set)
 ;max_open_conn =
 ;max_open_conn =
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+;conn_max_lifetime = 14400
+
 # Set to true to log the sql calls and execution times.
 # Set to true to log the sql calls and execution times.
 log_queries =
 log_queries =
 
 
@@ -121,7 +124,6 @@ log_queries =
 # This enables data proxy logging, default is false
 # This enables data proxy logging, default is false
 ;logging = false
 ;logging = false
 
 
-
 #################################### Analytics ####################################
 #################################### Analytics ####################################
 [analytics]
 [analytics]
 # Server reporting, sends usage counters to stats.grafana.org every 24 hours.
 # Server reporting, sends usage counters to stats.grafana.org every 24 hours.
@@ -323,7 +325,6 @@ log_queries =
 # optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
 # optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
 ;filters =
 ;filters =
 
 
-
 # For "console" mode only
 # For "console" mode only
 [log.console]
 [log.console]
 ;level =
 ;level =
@@ -369,7 +370,6 @@ log_queries =
 # Syslog tag. By default, the process' argv[0] is used.
 # Syslog tag. By default, the process' argv[0] is used.
 ;tag =
 ;tag =
 
 
-
 #################################### Alerting ############################
 #################################### Alerting ############################
 [alerting]
 [alerting]
 # Disable alerting engine & UI features
 # Disable alerting engine & UI features

+ 7 - 0
docker/blocks/elastic/docker-compose.yaml

@@ -6,3 +6,10 @@
       - "9300:9300"
       - "9300:9300"
     volumes:
     volumes:
       - ./blocks/elastic/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
       - ./blocks/elastic/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
+
+  fake-elastic-data:
+    image: grafana/fake-data-gen
+    network_mode: bridge
+    environment:
+      FD_DATASOURCE: elasticsearch
+      FD_PORT: 9200

+ 7 - 0
docker/blocks/elastic5/docker-compose.yaml

@@ -6,3 +6,10 @@
     ports:
     ports:
       - "10200:9200"
       - "10200:9200"
       - "10300:9300"
       - "10300:9300"
+
+  fake-elastic5-data:
+    image: grafana/fake-data-gen
+    network_mode: bridge
+    environment:
+      FD_DATASOURCE: elasticsearch
+      FD_PORT: 10200

+ 1161 - 0
docker/blocks/graphite1/big-dashboard.json

@@ -0,0 +1,1161 @@
+{
+  "__inputs": [
+    {
+      "name": "DS_GRAPHITE",
+      "label": "Graphite",
+      "description": "",
+      "type": "datasource",
+      "pluginId": "graphite",
+      "pluginName": "Graphite"
+    }
+  ],
+  "__requires": [
+    {
+      "type": "grafana",
+      "id": "grafana",
+      "name": "Grafana",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "graph",
+      "name": "Graph",
+      "version": "5.0.0"
+    },
+    {
+      "type": "datasource",
+      "id": "graphite",
+      "name": "Graphite",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "singlestat",
+      "name": "Singlestat",
+      "version": "5.0.0"
+    }
+  ],
+  "annotations": {
+    "enable": false,
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      }
+    ]
+  },
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 1,
+  "id": null,
+  "links": [],
+  "panels": [
+    {
+      "aliasColors": {
+        "cpu": "#E24D42",
+        "memory": "#1f78c1",
+        "statsd.fakesite.counters.session_start.desktop.count": "#6ED0E0"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "fill": 3,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 0
+      },
+      "id": 4,
+      "interactive": true,
+      "legend": {
+        "avg": false,
+        "current": true,
+        "max": false,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 2,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [
+        {
+          "alias": "cpu",
+          "fill": 0,
+          "lines": true,
+          "yaxis": 2,
+          "zindex": 2
+        },
+        {
+          "alias": "memory",
+          "pointradius": 2,
+          "points": true
+        }
+      ],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "hide": false,
+          "refId": "A",
+          "target": "alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), 'cpu')"
+        },
+        {
+          "refId": "B",
+          "target": "alias(statsd.fakesite.counters.session_start.desktop.count, 'memory')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "Memory / CPU",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "bytes",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "percent",
+          "logBase": 1,
+          "max": null,
+          "min": 0,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "aliasColors": {
+        "logins": "#5195ce",
+        "logins (-1 day)": "#447EBC",
+        "logins (-1 hour)": "#705da0"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "fill": 1,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 0
+      },
+      "id": 3,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": false,
+        "avg": false,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 1,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refId": "A",
+          "target": "alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), 2), 'logins')"
+        },
+        {
+          "refId": "B",
+          "target": "alias(movingAverage(timeShift(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), '1h'), 2), 'logins (-1 hour)')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": "1h",
+      "timezone": "browser",
+      "title": "logins",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "bytes",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 4,
+        "x": 16,
+        "y": 0
+      },
+      "id": 22,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_01.counters.requests.count, 0.4)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Memory",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 0
+      },
+      "id": 16,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "apps.backend.backend_02.counters.requests.count"
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Sign ups",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 3
+      },
+      "id": 17,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "apps.backend.backend_04.counters.requests.count"
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Sign outs",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 6
+      },
+      "id": 15,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_01.counters.requests.count, 0.7)"
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Logins",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "aliasColors": {
+        "web_server_01": "#badff4",
+        "web_server_02": "#5195ce",
+        "web_server_03": "#1f78c1",
+        "web_server_04": "#0a437c"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "fill": 6,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 11,
+        "w": 16,
+        "x": 0,
+        "y": 7
+      },
+      "id": 2,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": false,
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "rightSide": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 1,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refId": "A",
+          "target": "aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "server requests",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": true,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 5,
+        "w": 4,
+        "x": 16,
+        "y": 7
+      },
+      "id": 21,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_01.counters.requests.count, 0.8)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Logouts",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 9
+      },
+      "id": 18,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_03.counters.requests.count, 0.3)"
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Support calls",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 16,
+        "y": 12
+      },
+      "id": 26,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_01.counters.requests.count, 0.2)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Google hits",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 20,
+        "y": 12
+      },
+      "id": 24,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refId": "A",
+          "target": "scale(apps.backend.backend_01.counters.requests.count, 0.2)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Google hits",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "aliasColors": {
+        "upper_25": "#F9E2D2",
+        "upper_50": "#F2C96D",
+        "upper_75": "#EAB839"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE}",
+      "editable": true,
+      "fill": 1,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 11,
+        "w": 24,
+        "x": 0,
+        "y": 18
+      },
+      "id": 5,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": false,
+        "max": false,
+        "min": false,
+        "rightSide": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "legend_counts": true,
+      "lines": false,
+      "linewidth": 2,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refId": "A",
+          "target": "aliasByNode(summarize(statsd.fakesite.timers.ads_timer.*, '4min', 'avg'), 4)"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "client side full page load",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ms",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    }
+  ],
+  "refresh": false,
+  "schemaVersion": 16,
+  "style": "dark",
+  "tags": [
+    "demo"
+  ],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "collapse": false,
+    "enable": true,
+    "notice": false,
+    "now": true,
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "status": "Stable",
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "2h",
+      " 6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ],
+    "type": "timepicker"
+  },
+  "timezone": "browser",
+  "title": "Big Dashboard",
+  "uid": "000000003",
+  "version": 1
+}

+ 1179 - 0
docker/blocks/graphite11/big-dashboard.json

@@ -0,0 +1,1179 @@
+{
+  "__inputs": [
+    {
+      "name": "DS_GRAPHITE_1.1+",
+      "label": "Graphite 1.1+",
+      "description": "",
+      "type": "datasource",
+      "pluginId": "graphite",
+      "pluginName": "Graphite"
+    }
+  ],
+  "__requires": [
+    {
+      "type": "grafana",
+      "id": "grafana",
+      "name": "Grafana",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "graph",
+      "name": "Graph",
+      "version": "5.0.0"
+    },
+    {
+      "type": "datasource",
+      "id": "graphite",
+      "name": "Graphite",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "singlestat",
+      "name": "Singlestat",
+      "version": "5.0.0"
+    }
+  ],
+  "annotations": {
+    "enable": false,
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      }
+    ]
+  },
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 1,
+  "id": null,
+  "links": [],
+  "panels": [
+    {
+      "aliasColors": {
+        "cpu": "#E24D42",
+        "memory": "#1f78c1",
+        "statsd.fakesite.counters.session_start.desktop.count": "#6ED0E0"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "fill": 3,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 0
+      },
+      "id": 4,
+      "interactive": true,
+      "legend": {
+        "avg": false,
+        "current": true,
+        "max": false,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 2,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [
+        {
+          "alias": "cpu",
+          "fill": 0,
+          "lines": true,
+          "yaxis": 2,
+          "zindex": 2
+        },
+        {
+          "alias": "memory",
+          "pointradius": 2,
+          "points": true
+        }
+      ],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "C",
+          "target": "alias(movingAverage(scaleToSeconds(seriesByTag('name=request_status.count', 'status=code_302', 'app=fakesite', 'server=web_server_01'), 10), 20, 0), 'cpu')"
+        },
+        {
+          "refCount": 0,
+          "refId": "A",
+          "target": "alias(seriesByTag('name=statsd.counters.session_start', 'app=fakesite', 'device=desktop'), 'memory')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "Memory / CPU",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "bytes",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "percent",
+          "logBase": 1,
+          "max": null,
+          "min": 0,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "aliasColors": {
+        "logins": "#5195ce",
+        "logins (-1 day)": "#447EBC",
+        "logins (-1 hour)": "#705da0"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "fill": 1,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 0
+      },
+      "id": 3,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": false,
+        "avg": false,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 1,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "C",
+          "target": "alias(movingAverage(scaleToSeconds(seriesByTag('name=requests.count', 'app=fakesite', 'server=web_server_01'), 1), 2, 0), 'logins')"
+        },
+        {
+          "refCount": 0,
+          "refId": "A",
+          "target": "alias(movingAverage(timeShift(scaleToSeconds(seriesByTag('name=requests.count', 'app=fakesite', 'server=web_server_01'), 1), '1h'), 2, 0), 'logins (-1 hour)')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": "1h",
+      "timezone": "browser",
+      "title": "logins",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "bytes",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 4,
+        "x": 16,
+        "y": 0
+      },
+      "id": 22,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('app=backend', 'server=backend_01', 'name=requests.count'), 0.4)",
+          "textEditor": false
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Memory",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 0
+      },
+      "id": 16,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "seriesByTag('app=backend', 'server=backend_02', 'name=requests.count')"
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Sign ups",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 3
+      },
+      "id": 17,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "seriesByTag('name=requests.count', 'app=backend', 'server=backend_04')",
+          "textEditor": false
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Sign outs",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 6
+      },
+      "id": 15,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('name=requests.count', 'app=backend', 'server=backend_01'), 0.7)",
+          "textEditor": false
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Logins",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "aliasColors": {
+        "web_server_01": "#badff4",
+        "web_server_02": "#5195ce",
+        "web_server_03": "#1f78c1",
+        "web_server_04": "#0a437c"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "fill": 6,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 11,
+        "w": 16,
+        "x": 0,
+        "y": 7
+      },
+      "id": 2,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": false,
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "rightSide": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "legend_counts": true,
+      "lines": true,
+      "linewidth": 1,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "aliasByTags(movingAverage(scaleToSeconds(seriesByTag('name=requests.count', 'app=fakesite', 'target='), 1), 2, 0), 'server')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "server requests",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": true,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 5,
+        "w": 4,
+        "x": 16,
+        "y": 7
+      },
+      "id": 21,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "hide": false,
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('name=requests.count', 'app=backend', 'server=backend_01'), 0.8)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Logouts",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "rgba(245, 54, 54, 0.9)",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(50, 172, 45, 0.97)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 9
+      },
+      "id": 18,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('name=requests.count', 'app=backend', 'server=backend_03'), 0.3)",
+          "textEditor": false
+        }
+      ],
+      "thresholds": "100,270",
+      "title": "Support calls",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 16,
+        "y": 12
+      },
+      "id": 26,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('name=requests.count', 'app=backend', 'server=backend_01'), 0.2)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Google hits",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#629e51",
+        "rgba(237, 129, 40, 0.89)",
+        "rgba(245, 54, 54, 0.9)"
+      ],
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "error": false,
+      "format": "none",
+      "gauge": {
+        "maxValue": 300,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 20,
+        "y": 12
+      },
+      "id": 24,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "scale(seriesByTag('name=requests.count', 'app=backend', 'server=backend_01'), 0.2)"
+        }
+      ],
+      "thresholds": "200,270",
+      "title": "Google hits",
+      "type": "singlestat",
+      "valueFontSize": "100%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "aliasColors": {
+        "upper_25": "#F9E2D2",
+        "upper_50": "#F2C96D",
+        "upper_75": "#EAB839"
+      },
+      "annotate": {
+        "enable": false
+      },
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_GRAPHITE_1.1+}",
+      "editable": true,
+      "fill": 1,
+      "grid": {
+        "max": null,
+        "min": 0
+      },
+      "gridPos": {
+        "h": 11,
+        "w": 24,
+        "x": 0,
+        "y": 18
+      },
+      "id": 5,
+      "interactive": true,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": false,
+        "max": false,
+        "min": false,
+        "rightSide": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "legend_counts": true,
+      "lines": false,
+      "linewidth": 2,
+      "nullPointMode": "connected",
+      "options": false,
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "resolution": 100,
+      "scale": 1,
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "spyable": true,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "refCount": 0,
+          "refId": "B",
+          "target": "aliasByTags(summarize(seriesByTag('name=statsd.timers.ads_timer', 'app=fakesite', 'percentile='), '4min', 'average'), 'target')"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "timezone": "browser",
+      "title": "client side full page load",
+      "tooltip": {
+        "msResolution": false,
+        "query_as_alias": true,
+        "shared": false,
+        "sort": 0,
+        "value_type": "cumulative"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ms",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "zerofill": true
+    }
+  ],
+  "refresh": false,
+  "schemaVersion": 16,
+  "style": "dark",
+  "tags": [
+    "demo"
+  ],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "collapse": false,
+    "enable": true,
+    "notice": false,
+    "now": true,
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "status": "Stable",
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "2h",
+      " 6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ],
+    "type": "timepicker"
+  },
+  "timezone": "browser",
+  "title": "Big Dashboard",
+  "uid": "yNSrbLRmz",
+  "version": 9
+}

+ 18 - 0
docker/blocks/graphite11/docker-compose.yaml

@@ -0,0 +1,18 @@
+  graphite11:
+    image: graphiteapp/graphite-statsd
+    ports:
+      - "8180:80"
+      - "2103-2104:2003-2004"
+      - "2123-2124:2023-2024"
+      - "8225:8125/udp"
+      - "8226:8126"
+
+  fake-graphite11-data:
+    image: grafana/fake-data-gen
+    network_mode: bridge
+    environment:
+      FD_DATASOURCE: graphite
+      FD_PORT: 2103
+      FD_GRAPHITE_VERSION: 1.1
+    depends_on:
+      - graphite11

+ 5 - 0
docker/blocks/mssql/build/Dockerfile

@@ -0,0 +1,5 @@
+FROM microsoft/mssql-server-linux:2017-CU4
+WORKDIR /usr/setup
+COPY . /usr/setup
+RUN chmod +x /usr/setup/setup.sh
+CMD /bin/bash ./entrypoint.sh

+ 2 - 0
docker/blocks/mssql/build/entrypoint.sh

@@ -0,0 +1,2 @@
+#start SQL Server and run setup script
+/usr/setup/setup.sh & /opt/mssql/bin/sqlservr

+ 12 - 0
docker/blocks/mssql/build/setup.sh

@@ -0,0 +1,12 @@
+#/bin/bash
+
+#wait for the SQL Server to come up
+sleep 15s
+
+cat /usr/setup/setup.sql.template | awk '{
+  gsub(/%%DB%%/,"'$MSSQL_DATABASE'");
+  gsub(/%%USER%%/,"'$MSSQL_USER'");
+  gsub(/%%PWD%%/,"'$MSSQL_PASSWORD'")
+}1' > /usr/setup/setup.sql
+
+/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $MSSQL_SA_PASSWORD -d master -i /usr/setup/setup.sql

+ 14 - 0
docker/blocks/mssql/build/setup.sql.template

@@ -0,0 +1,14 @@
+CREATE LOGIN %%USER%% WITH PASSWORD = '%%PWD%%'
+GO
+
+CREATE DATABASE %%DB%%;
+GO
+
+USE %%DB%%;
+GO
+
+CREATE USER %%USER%% FOR LOGIN %%USER%%;
+GO
+
+EXEC sp_addrolemember 'db_owner', '%%USER%%';
+GO

+ 539 - 0
docker/blocks/mssql/dashboard.json

@@ -0,0 +1,539 @@
+{
+  "__inputs": [
+    {
+      "name": "DS_MSSQL",
+      "label": "MSSQL",
+      "description": "",
+      "type": "datasource",
+      "pluginId": "mssql",
+      "pluginName": "MSSQL"
+    }
+  ],
+  "__requires": [
+    {
+      "type": "grafana",
+      "id": "grafana",
+      "name": "Grafana",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "graph",
+      "name": "Graph",
+      "version": "5.0.0"
+    },
+    {
+      "type": "datasource",
+      "id": "mssql",
+      "name": "MSSQL",
+      "version": "1.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "table",
+      "name": "Table",
+      "version": "5.0.0"
+    }
+  ],
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "A dashboard visualizing data generated from grafana/fake-data-gen",
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 0,
+  "id": null,
+  "iteration": 1520976748896,
+  "links": [],
+  "panels": [
+    {
+      "aliasColors": {
+        "total avg": "#6ed0e0"
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 0
+      },
+      "id": 2,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [
+        {
+          "alias": "total avg",
+          "fill": 0,
+          "pointradius": 3,
+          "points": true
+        }
+      ],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT\n  $__timeGroup(createdAt,'$summarize') as time,\n  avg(value) as value,\n  hostname as metric\nFROM \n  grafana_metric\nWHERE\n  $__timeFilter(createdAt) AND\n  measurement = 'logins.count' AND\n  hostname IN($host)\nGROUP BY $__timeGroup(createdAt,'$summarize'), hostname\nORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT\n  $__timeGroup(createdAt,'$summarize') as time,\n  min(value) as value,\n  'total avg' as metric\nFROM \n  grafana_metric\nWHERE\n  $__timeFilter(createdAt) AND\n  measurement = 'logins.count'\nGROUP BY $__timeGroup(createdAt,'$summarize')\nORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Average logins / $summarize",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL}",
+      "fill": 2,
+      "gridPos": {
+        "h": 18,
+        "w": 12,
+        "x": 12,
+        "y": 0
+      },
+      "id": 8,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT\n  $__timeGroup(createdAt,'$summarize') as time,\n  avg(value) as value,\n  'started' as metric\nFROM \n  grafana_metric\nWHERE\n  $__timeFilter(createdAt) AND\n  measurement = 'payment.started'\nGROUP BY $__timeGroup(createdAt,'$summarize')\nORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT\n  $__timeGroup(createdAt,'$summarize') as time,\n  avg(value) as value,\n  'ended' as \"metric\"\nFROM \n  grafana_metric\nWHERE\n  $__timeFilter(createdAt) AND\n  measurement = 'payment.ended'\nGROUP BY $__timeGroup(createdAt,'$summarize')\nORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Average payments started/ended / $summarize",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 9
+      },
+      "id": 6,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT\n  $__timeGroup(createdAt,'$summarize') as time,\n  max(value) as value,\n  hostname as metric\nFROM \n  grafana_metric\nWHERE\n  $__timeFilter(createdAt) AND\n  measurement = 'cpu' AND\n  hostname IN($host)\nGROUP BY $__timeGroup(createdAt,'$summarize'), hostname\nORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Max CPU / $summarize",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "columns": [],
+      "datasource": "${DS_MSSQL}",
+      "fontSize": "100%",
+      "gridPos": {
+        "h": 10,
+        "w": 24,
+        "x": 0,
+        "y": 18
+      },
+      "id": 4,
+      "links": [],
+      "pageSize": null,
+      "scroll": true,
+      "showHeader": true,
+      "sort": {
+        "col": 0,
+        "desc": true
+      },
+      "styles": [
+        {
+          "alias": "Time",
+          "dateFormat": "YYYY-MM-DD HH:mm:ss",
+          "pattern": "Time",
+          "type": "date"
+        },
+        {
+          "alias": "",
+          "colorMode": null,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "decimals": 2,
+          "pattern": "/.*/",
+          "thresholds": [],
+          "type": "number",
+          "unit": "short"
+        }
+      ],
+      "targets": [
+        {
+          "alias": "",
+          "format": "table",
+          "rawSql": "SELECT createdAt as Time, source, datacenter, hostname, value FROM grafana_metric WHERE hostname in($host)",
+          "refId": "A"
+        }
+      ],
+      "title": "Values",
+      "transform": "table",
+      "type": "table"
+    }
+  ],
+  "schemaVersion": 16,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "${DS_MSSQL}",
+        "hide": 0,
+        "includeAll": false,
+        "label": "Datacenter",
+        "multi": false,
+        "name": "datacenter",
+        "options": [],
+        "query": "SELECT DISTINCT datacenter FROM grafana_metric",
+        "refresh": 1,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "${DS_MSSQL}",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Hostname",
+        "multi": true,
+        "name": "host",
+        "options": [],
+        "query": "SELECT DISTINCT hostname FROM grafana_metric WHERE datacenter='$datacenter'",
+        "refresh": 1,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "auto": false,
+        "auto_count": 30,
+        "auto_min": "10s",
+        "current": {
+          "text": "1m",
+          "value": "1m"
+        },
+        "hide": 0,
+        "label": "Summarize",
+        "name": "summarize",
+        "options": [
+          {
+            "selected": false,
+            "text": "1s",
+            "value": "1s"
+          },
+          {
+            "selected": false,
+            "text": "10s",
+            "value": "10s"
+          },
+          {
+            "selected": false,
+            "text": "30s",
+            "value": "30s"
+          },
+          {
+            "selected": true,
+            "text": "1m",
+            "value": "1m"
+          },
+          {
+            "selected": false,
+            "text": "5m",
+            "value": "5m"
+          },
+          {
+            "selected": false,
+            "text": "10m",
+            "value": "10m"
+          },
+          {
+            "selected": false,
+            "text": "30m",
+            "value": "30m"
+          },
+          {
+            "selected": false,
+            "text": "1h",
+            "value": "1h"
+          },
+          {
+            "selected": false,
+            "text": "6h",
+            "value": "6h"
+          },
+          {
+            "selected": false,
+            "text": "12h",
+            "value": "12h"
+          },
+          {
+            "selected": false,
+            "text": "1d",
+            "value": "1d"
+          },
+          {
+            "selected": false,
+            "text": "7d",
+            "value": "7d"
+          },
+          {
+            "selected": false,
+            "text": "14d",
+            "value": "14d"
+          },
+          {
+            "selected": false,
+            "text": "30d",
+            "value": "30d"
+          }
+        ],
+        "query": "1s,10s,30s,1m,5m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
+        "refresh": 2,
+        "type": "interval"
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "",
+  "title": "Grafana Fake Data Gen - MSSQL",
+  "uid": "86Js1xRmk",
+  "version": 11
+}

+ 19 - 0
docker/blocks/mssql/docker-compose.yaml

@@ -0,0 +1,19 @@
+  mssql:
+    build:
+      context: blocks/mssql/build
+    environment:
+      ACCEPT_EULA: Y
+      MSSQL_SA_PASSWORD: Password!
+      MSSQL_PID: Express
+      MSSQL_DATABASE: grafana
+      MSSQL_USER: grafana
+      MSSQL_PASSWORD: Password!
+    ports:
+      - "1433:1433"
+
+  fake-mssql-data:
+    image: grafana/fake-data-gen
+    network_mode: bridge
+    environment:
+      FD_DATASOURCE: mssql
+      FD_PORT: 1433

+ 2242 - 0
docker/blocks/mssql_tests/dashboard.json

@@ -0,0 +1,2242 @@
+{
+  "__inputs": [
+    {
+      "name": "DS_MSSQL_TEST",
+      "label": "MSSQL Test",
+      "description": "",
+      "type": "datasource",
+      "pluginId": "mssql",
+      "pluginName": "Microsoft SQL Server"
+    }
+  ],
+  "__requires": [
+    {
+      "type": "grafana",
+      "id": "grafana",
+      "name": "Grafana",
+      "version": "5.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "graph",
+      "name": "Graph",
+      "version": "5.0.0"
+    },
+    {
+      "type": "datasource",
+      "id": "mssql",
+      "name": "Microsoft SQL Server",
+      "version": "1.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "table",
+      "name": "Table",
+      "version": "5.0.0"
+    }
+  ],
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      },
+      {
+        "datasource": "${DS_MSSQL_TEST}",
+        "enable": false,
+        "hide": false,
+        "iconColor": "#6ed0e0",
+        "limit": 100,
+        "name": "Deploys",
+        "rawQuery": "SELECT\n   time_sec as time,\n   description as [text],\n   tags\n  FROM [event]\n  WHERE $__unixEpochFilter(time_sec) AND tags='deploy'\n  ORDER BY 1 ASC\n  ",
+        "showIn": 0,
+        "tags": [],
+        "type": "tags"
+      },
+      {
+        "datasource": "${DS_MSSQL_TEST}",
+        "enable": false,
+        "hide": false,
+        "iconColor": "rgba(255, 96, 96, 1)",
+        "limit": 100,
+        "name": "Tickets",
+        "rawQuery": "SELECT\n   time_sec as time,\n   description as [text],\n   tags\n  FROM [event]\n  WHERE $__unixEpochFilter(time_sec) AND tags='ticket'\n  ORDER BY 1 ASC\n  ",
+        "showIn": 0,
+        "tags": [],
+        "type": "tags"
+      },
+      {
+        "datasource": "${DS_MSSQL_TEST}",
+        "enable": false,
+        "hide": false,
+        "iconColor": "#7eb26d",
+        "limit": 100,
+        "name": "Metric Values",
+        "rawQuery": "SELECT \n  time, \n  measurement as text, \n  '' as tags\nFROM\n  metric_values \nORDER BY 1",
+        "showIn": 0,
+        "tags": [],
+        "type": "tags"
+      }
+    ]
+  },
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 0,
+  "id": null,
+  "iteration": 1521481503341,
+  "links": [],
+  "panels": [
+    {
+      "columns": [],
+      "datasource": "${DS_MSSQL_TEST}",
+      "fontSize": "100%",
+      "gridPos": {
+        "h": 4,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 2,
+      "links": [],
+      "pageSize": null,
+      "scroll": true,
+      "showHeader": true,
+      "sort": {
+        "col": 0,
+        "desc": true
+      },
+      "styles": [
+        {
+          "alias": "",
+          "colorMode": null,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "decimals": 2,
+          "pattern": "/.*/",
+          "thresholds": [],
+          "type": "string",
+          "unit": "short"
+        }
+      ],
+      "targets": [
+        {
+          "alias": "",
+          "format": "table",
+          "rawSql": "SELECT * from mssql_types",
+          "refId": "A"
+        }
+      ],
+      "title": "Data types",
+      "transform": "table",
+      "type": "table"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 0,
+        "y": 4
+      },
+      "id": 7,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": true,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '5m') AS time, avg(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "timeGroup macro 5m without fill",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 8,
+        "y": 4
+      },
+      "id": 9,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null as zero",
+      "percentage": false,
+      "pointradius": 3,
+      "points": true,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '5m', NULL) AS time, avg(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "timeGroup macro 5m with fill(NULL) and null as zero",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 16,
+        "y": 4
+      },
+      "id": 10,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": true,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '5m', 10.0) AS time, avg(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "timeGroup macro 5m with fill(10.0)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 0,
+        "y": 13
+      },
+      "id": 16,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": false,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '$summarize') AS time, avg(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '$summarize') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Metrics - timeGroup macro $summarize without fill",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 8,
+        "y": 13
+      },
+      "id": 12,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": false,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null as zero",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '$summarize', NULL) AS time, sum(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '$summarize') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Metrics - timeGroup macro $summarize with fill(NULL)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 16,
+        "y": 13
+      },
+      "id": 13,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": false,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": true,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeGroup(time, '$summarize', 100.0) AS time, sum(value) as value FROM metric WHERE $__timeFilter(time) GROUP BY $__timeGroup(time, '$summarize') ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Metrics - timeGroup macro $summarize with fill(100.0)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 22
+      },
+      "id": 27,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT \n  $__timeGroup(time, '$summarize') as time, \n  measurement + ' - value one' as metric, \n  avg(valueOne) as valueOne\nFROM\n  metric_values \nWHERE\n  $__timeFilter(time)\nGROUP BY \n  $__timeGroup(time, '$summarize'), \n  measurement \nORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT \n  $__timeGroup(time, '$summarize') as time, \n  measurement + ' - value two' as metric, \n  avg(valueTwo) as valueTwo \nFROM\n  metric_values \nGROUP BY \n  $__timeGroup(time, '$summarize'), \n  measurement \nORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column using timeGroup macro ($summarize)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 22
+      },
+      "id": 5,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT \n  $__timeGroup(time, '$summarize') as time, \n  avg(valueOne) as valueOne, \n  avg(valueTwo) as valueTwo \nFROM\n  metric_values \nGROUP BY \n  $__timeGroup(time, '$summarize')\nORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column using timeGroup macro ($summarize)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 30
+      },
+      "id": 4,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 30
+      },
+      "id": 28,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 38
+      },
+      "id": 19,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - stacked",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 38
+      },
+      "id": 18,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - stacked",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 46
+      },
+      "id": 17,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": true,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - stacked percent",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 46
+      },
+      "id": 20,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": true,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - stacked percent",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 54
+      },
+      "id": 29,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "DECLARE \n  @from int = $__unixEpochFrom(),\n  @to int = $__unixEpochTo()\n  \nEXEC dbo.sp_test_epoch @from, @to",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Stored procedure support using epoch",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 2,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 54
+      },
+      "id": 30,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 3,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "DECLARE \n  @from datetime = $__timeFrom(),\n  @to datetime = $__timeTo()\n  \nEXEC dbo.sp_test_datetime @from, @to",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Stored procedure support using datetime",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 62
+      },
+      "id": 14,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - series mode",
+      "tooltip": {
+        "shared": false,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "series",
+        "name": null,
+        "show": true,
+        "values": [
+          "total"
+        ]
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 62
+      },
+      "id": 15,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - series mode",
+      "tooltip": {
+        "shared": false,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "series",
+        "name": null,
+        "show": true,
+        "values": [
+          "total"
+        ]
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 70
+      },
+      "id": 25,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - histogram",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 50,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "current"
+        ]
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 70
+      },
+      "id": 22,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - histogram",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 100,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "total"
+        ]
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 78
+      },
+      "id": 21,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - histogram stacked",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 20,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "current"
+        ]
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 78
+      },
+      "id": 26,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - histogram stacked",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 20,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "total"
+        ]
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 86
+      },
+      "id": 23,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": true,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
+          "refId": "A"
+        },
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value two' as metric, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series with metric column - histogram stacked percent",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 20,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "current"
+        ]
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    },
+    {
+      "aliasColors": {},
+      "bars": true,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_MSSQL_TEST}",
+      "fill": 1,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 86
+      },
+      "id": 24,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "total": true,
+        "values": true
+      },
+      "lines": false,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "percentage": true,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "format": "time_series",
+          "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Multiple series without metric column - histogram stacked percent",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": 20,
+        "mode": "histogram",
+        "name": null,
+        "show": true,
+        "values": [
+          "total"
+        ]
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ]
+    }
+  ],
+  "refresh": false,
+  "schemaVersion": 16,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "auto": false,
+        "auto_count": 30,
+        "auto_min": "10s",
+        "current": {
+          "text": "10m",
+          "value": "10m"
+        },
+        "hide": 0,
+        "label": "Interval",
+        "name": "summarize",
+        "options": [
+          {
+            "selected": false,
+            "text": "1s",
+            "value": "1s"
+          },
+          {
+            "selected": false,
+            "text": "10s",
+            "value": "10s"
+          },
+          {
+            "selected": false,
+            "text": "30s",
+            "value": "30s"
+          },
+          {
+            "selected": false,
+            "text": "1m",
+            "value": "1m"
+          },
+          {
+            "selected": false,
+            "text": "5m",
+            "value": "5m"
+          },
+          {
+            "selected": true,
+            "text": "10m",
+            "value": "10m"
+          }
+        ],
+        "query": "1s,10s,30s,1m,5m,10m",
+        "refresh": 2,
+        "type": "interval"
+      }
+    ]
+  },
+  "time": {
+    "from": "2018-03-15T12:30:00.000Z",
+    "to": "2018-03-15T13:55:00.000Z"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "",
+  "title": "Microsoft SQL Server Data Source Test",
+  "uid": "GlAqcPgmz",
+  "version": 37
+}

+ 12 - 0
docker/blocks/mssql_tests/docker-compose.yaml

@@ -0,0 +1,12 @@
+  mssqltests:
+    build:
+      context: blocks/mssql/build
+    environment:
+      ACCEPT_EULA: Y
+      MSSQL_SA_PASSWORD: Password!
+      MSSQL_PID: Express
+      MSSQL_DATABASE: grafanatest
+      MSSQL_USER: grafana
+      MSSQL_PASSWORD: Password!
+    ports:
+      - "1433:1433"

+ 6 - 0
docker/blocks/prometheus/docker-compose.yaml

@@ -23,3 +23,9 @@
     network_mode: host
     network_mode: host
     ports:
     ports:
       - "9093:9093"
       - "9093:9093"
+
+  prometheus-random-data:
+    build: blocks/prometheus_random_data
+    network_mode: host
+    ports:
+      - "8081:8080"

+ 6 - 2
docker/blocks/prometheus/prometheus.yml

@@ -25,11 +25,15 @@ scrape_configs:
   - job_name: 'node_exporter'
   - job_name: 'node_exporter'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:9100']
       - targets: ['127.0.0.1:9100']
- 
+
   - job_name: 'fake-data-gen'
   - job_name: 'fake-data-gen'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:9091']
       - targets: ['127.0.0.1:9091']
-  
+
   - job_name: 'grafana'
   - job_name: 'grafana'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:3000']
       - targets: ['127.0.0.1:3000']
+
+  - job_name: 'prometheus-random-data'
+    static_configs:
+      - targets: ['127.0.0.1:8081']

+ 1 - 1
docker/blocks/prometheus2/Dockerfile

@@ -1,3 +1,3 @@
-FROM prom/prometheus:v2.0.0
+FROM prom/prometheus:v2.2.0
 ADD prometheus.yml /etc/prometheus/
 ADD prometheus.yml /etc/prometheus/
 ADD alert.rules /etc/prometheus/
 ADD alert.rules /etc/prometheus/

+ 6 - 0
docker/blocks/prometheus2/docker-compose.yaml

@@ -23,3 +23,9 @@
     network_mode: host
     network_mode: host
     ports:
     ports:
       - "9093:9093"
       - "9093:9093"
+
+  prometheus-random-data:
+    build: blocks/prometheus_random_data
+    network_mode: host
+    ports:
+      - "8081:8080"

+ 6 - 2
docker/blocks/prometheus2/prometheus.yml

@@ -25,11 +25,15 @@ scrape_configs:
   - job_name: 'node_exporter'
   - job_name: 'node_exporter'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:9100']
       - targets: ['127.0.0.1:9100']
- 
+
   - job_name: 'fake-data-gen'
   - job_name: 'fake-data-gen'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:9091']
       - targets: ['127.0.0.1:9091']
-  
+
   - job_name: 'grafana'
   - job_name: 'grafana'
     static_configs:
     static_configs:
       - targets: ['127.0.0.1:3000']
       - targets: ['127.0.0.1:3000']
+
+  - job_name: 'prometheus-random-data'
+    static_configs:
+      - targets: ['127.0.0.1:8081']

+ 18 - 0
docker/blocks/prometheus_random_data/Dockerfile

@@ -0,0 +1,18 @@
+# This Dockerfile builds an image for a client_golang example.
+
+# Builder image, where we build the example.
+FROM golang:1.9.0 AS builder
+# Download prometheus/client_golang/examples/random first
+RUN go get github.com/prometheus/client_golang/examples/random
+WORKDIR /go/src/github.com/prometheus/client_golang
+WORKDIR /go/src/github.com/prometheus/client_golang/prometheus
+RUN go get -d
+WORKDIR /go/src/github.com/prometheus/client_golang/examples/random
+RUN CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags '-w'
+
+# Final image.
+FROM scratch
+LABEL maintainer "The Prometheus Authors <prometheus-developers@googlegroups.com>"
+COPY --from=builder /go/src/github.com/prometheus/client_golang/examples/random .
+EXPOSE 8080
+ENTRYPOINT ["/random"]

+ 9 - 3
docs/sources/administration/provisioning.md

@@ -133,18 +133,24 @@ datasources:
   editable: false
   editable: false
 ```
 ```
 
 
+#### Extra info per datasource
+
+| Datasource | Misc |
+| ---- | ---- |
+| Elasticserach | Elasticsearch uses the `database` property to configure the index for a datasource |
+
 #### Json data
 #### Json data
 
 
 Since not all datasources have the same configuration settings we only have the most common ones as fields. The rest should be stored as a json blob in the `json_data` field. Here are the most common settings that the core datasources use.
 Since not all datasources have the same configuration settings we only have the most common ones as fields. The rest should be stored as a json blob in the `json_data` field. Here are the most common settings that the core datasources use.
 
 
-| Name | Type | Datasource |Description |
-| ----| ---- | ---- | --- |
+| Name | Type | Datasource | Description |
+| ---- | ---- | ---- | ---- |
 | tlsAuth | boolean | *All* |  Enable TLS authentication using client cert configured in secure json data |
 | tlsAuth | boolean | *All* |  Enable TLS authentication using client cert configured in secure json data |
 | tlsAuthWithCACert | boolean | *All* | Enable TLS authtication using CA cert |
 | tlsAuthWithCACert | boolean | *All* | Enable TLS authtication using CA cert |
 | tlsSkipVerify | boolean | *All* | Controls whether a client verifies the server's certificate chain and host name. |
 | tlsSkipVerify | boolean | *All* | Controls whether a client verifies the server's certificate chain and host name. |
 | graphiteVersion | string | Graphite |  Graphite version  |
 | graphiteVersion | string | Graphite |  Graphite version  |
 | timeInterval | string | Elastic, Influxdb & Prometheus | Lowest interval/step value that should be used for this data source |
 | timeInterval | string | Elastic, Influxdb & Prometheus | Lowest interval/step value that should be used for this data source |
-| esVersion | string | Elastic | Elasticsearch version |
+| esVersion | string | Elastic | Elasticsearch version as an number (2/5/56) |
 | timeField | string | Elastic | Which field that should be used as timestamp |
 | timeField | string | Elastic | Which field that should be used as timestamp |
 | interval | string | Elastic | Index date time format |
 | interval | string | Elastic | Index date time format |
 | authType | string | Cloudwatch | Auth provider. keys/credentials/arn |
 | authType | string | Cloudwatch | Auth provider. keys/credentials/arn |

+ 2 - 0
docs/sources/alerting/notifications.md

@@ -58,6 +58,8 @@ Recipient | allows you to override the Slack recipient.
 Mention | make it possible to include a mention in the Slack notification sent by Grafana. Ex @here or @channel
 Mention | make it possible to include a mention in the Slack notification sent by Grafana. Ex @here or @channel
 Token | If provided, Grafana will upload the generated image via Slack's file.upload API method, not the external image destination.
 Token | If provided, Grafana will upload the generated image via Slack's file.upload API method, not the external image destination.
 
 
+If you are using the token for a slack bot, then you have to invite the bot to the channel you want to send notifications and add the channel to the recipient field.
+
 ### PagerDuty
 ### PagerDuty
 
 
 To set up PagerDuty, all you have to do is to provide an API key.
 To set up PagerDuty, all you have to do is to provide an API key.

+ 1 - 1
docs/sources/alerting/rules.md

@@ -59,7 +59,7 @@ avg() OF query(A, 5m, now) IS BELOW 14
 ```
 ```
 
 
 - `avg()` Controls how the values for **each** series should be reduced to a value that can be compared against the threshold. Click on the function to change it to another aggregation function.
 - `avg()` Controls how the values for **each** series should be reduced to a value that can be compared against the threshold. Click on the function to change it to another aggregation function.
-- `query(A, 5m, now)`  The letter defines what query to execute from the **Metrics** tab. The second two parameters define the time range, `5m, now` means 5 minutes from now to now. You can also do `10m, now-2m` to define a time range that will be 10 minutes from now to 2 minutes from now. This is useful if you want to ignore the last 2 minutes of data.
+- `query(A, 5m, now)`  The letter defines what query to execute from the **Metrics** tab. The second two parameters define the time range, `5m, now` means 5 minutes ago to now. You can also do `10m, now-2m` to define a time range that will be 10 minutes ago to 2 minutes ago. This is useful if you want to ignore the last 2 minutes of data.
 - `IS BELOW 14`  Defines the type of threshold and the threshold value.  You can click on `IS BELOW` to change the type of threshold.
 - `IS BELOW 14`  Defines the type of threshold and the threshold value.  You can click on `IS BELOW` to change the type of threshold.
 
 
 The query used in an alert rule cannot contain any template variables. Currently we only support `AND` and `OR` operators between conditions and they are executed serially.
 The query used in an alert rule cannot contain any template variables. Currently we only support `AND` and `OR` operators between conditions and they are executed serially.

+ 2 - 1
docs/sources/features/datasources/cloudwatch.md

@@ -87,7 +87,7 @@ Name | Description
 *namespaces()* | Returns a list of namespaces CloudWatch support.
 *namespaces()* | Returns a list of namespaces CloudWatch support.
 *metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region or use "default" for custom metrics)
 *metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region or use "default" for custom metrics)
 *dimension_keys(namespace)* | Returns a list of dimension keys in the namespace.
 *dimension_keys(namespace)* | Returns a list of dimension keys in the namespace.
-*dimension_values(region, namespace, metric, dimension_key)* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`.
+*dimension_values(region, namespace, metric, dimension_key, [filters])* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric`, `dimension_key` or you can use dimension `filters` to get more specific result as well.
 *ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`.
 *ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`.
 *ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attributes matching the specified `region`, `attribute_name`, `filters`.
 *ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attributes matching the specified `region`, `attribute_name`, `filters`.
 
 
@@ -104,6 +104,7 @@ Query | Service
 *dimension_values(us-east-1,AWS/Redshift,CPUUtilization,ClusterIdentifier)* | RedShift
 *dimension_values(us-east-1,AWS/Redshift,CPUUtilization,ClusterIdentifier)* | RedShift
 *dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS
 *dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS
 *dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3
 *dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3
+*dimension_values(us-east-1,CWAgent,disk_used_percent,device,{"InstanceId":"$instance_id"})* | CloudWatch Agent
 
 
 ## ec2_instance_attribute examples
 ## ec2_instance_attribute examples
 
 

+ 1 - 1
docs/sources/features/datasources/prometheus.md

@@ -93,7 +93,7 @@ queries via the Dashboard menu / Annotations view.
 Prometheus supports two ways to query annotations.
 Prometheus supports two ways to query annotations.
 
 
 - A regular metric query
 - A regular metric query
-- A Prometheus query for pending and firing alerts (for details see [Inspecting alerts during runtime](https://prometheus.io/docs/alerting/rules/#inspecting-alerts-during-runtime))
+- A Prometheus query for pending and firing alerts (for details see [Inspecting alerts during runtime](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/#inspecting-alerts-during-runtime))
 
 
 The step option is useful to limit the number of events returned from your query.
 The step option is useful to limit the number of events returned from your query.
 
 

+ 5 - 22
docs/sources/http_api/dashboard.md

@@ -43,11 +43,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
     "title": "Production Overview",
     "title": "Production Overview",
     "tags": [ "templated" ],
     "tags": [ "templated" ],
     "timezone": "browser",
     "timezone": "browser",
-    "rows": [
-      {
-      }
-    ],
-    "schemaVersion": 6,
+    "schemaVersion": 16,
     "version": 0
     "version": 0
   },
   },
   "folderId": 0,
   "folderId": 0,
@@ -140,11 +136,7 @@ Content-Type: application/json
     "title": "Production Overview",
     "title": "Production Overview",
     "tags": [ "templated" ],
     "tags": [ "templated" ],
     "timezone": "browser",
     "timezone": "browser",
-    "rows": [
-      {
-      }
-    ],
-    "schemaVersion": 6,
+    "schemaVersion": 16,
     "version": 0
     "version": 0
   },
   },
   "meta": {
   "meta": {
@@ -219,14 +211,9 @@ Content-Type: application/json
     "editable":false,
     "editable":false,
     "hideControls":true,
     "hideControls":true,
     "nav":[
     "nav":[
-    {
-      "enable":false,
-      "type":"timepicker"
-    }
-    ],
-    "rows": [
       {
       {
-
+        "enable":false,
+        "type":"timepicker"
       }
       }
     ],
     ],
     "style":"dark",
     "style":"dark",
@@ -322,11 +309,7 @@ Content-Type: application/json
     "title": "Production Overview",
     "title": "Production Overview",
     "tags": [ "templated" ],
     "tags": [ "templated" ],
     "timezone": "browser",
     "timezone": "browser",
-    "rows": [
-      {
-      }
-    ],
-    "schemaVersion": 6,
+    "schemaVersion": 16,
     "version": 0
     "version": 0
   },
   },
   "meta": {
   "meta": {

+ 1 - 0
docs/sources/http_api/index.md

@@ -31,6 +31,7 @@ dashboards, creating users and updating data sources.
 * [Annotations API]({{< relref "http_api/annotations.md" >}})
 * [Annotations API]({{< relref "http_api/annotations.md" >}})
 * [Alerting API]({{< relref "http_api/alerting.md" >}})
 * [Alerting API]({{< relref "http_api/alerting.md" >}})
 * [User API]({{< relref "http_api/user.md" >}})
 * [User API]({{< relref "http_api/user.md" >}})
+* [Team API]({{< relref "http_api/team.md" >}})
 * [Admin API]({{< relref "http_api/admin.md" >}})
 * [Admin API]({{< relref "http_api/admin.md" >}})
 * [Preferences API]({{< relref "http_api/preferences.md" >}})
 * [Preferences API]({{< relref "http_api/preferences.md" >}})
 * [Other API]({{< relref "http_api/other.md" >}})
 * [Other API]({{< relref "http_api/other.md" >}})

+ 5 - 0
docs/sources/installation/configuration.md

@@ -234,7 +234,12 @@ The maximum number of connections in the idle connection pool.
 ### max_open_conn
 ### max_open_conn
 The maximum number of open connections to the database.
 The maximum number of open connections to the database.
 
 
+### conn_max_lifetime
+
+Sets the maximum amount of time a connection may be reused. The default is 14400 (which means 14400 seconds or 4 hours). For MySQL, this setting should be shorter than the [`wait_timeout`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout) variable.
+
 ### log_queries
 ### log_queries
+
 Set to `true` to log the sql calls and execution times.
 Set to `true` to log the sql calls and execution times.
 
 
 <hr />
 <hr />

+ 3 - 3
docs/sources/installation/debian.md

@@ -15,7 +15,7 @@ weight = 1
 
 
 Description | Download
 Description | Download
 ------------ | -------------
 ------------ | -------------
-Stable for Debian-based Linux | [grafana_5.0.0_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0_amd64.deb)
+Stable for Debian-based Linux | [grafana_5.0.3_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.3_amd64.deb)
 
 
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 installation.
 installation.
@@ -24,9 +24,9 @@ installation.
 
 
 
 
 ```bash
 ```bash
-wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0_amd64.deb
+wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.3_amd64.deb
 sudo apt-get install -y adduser libfontconfig
 sudo apt-get install -y adduser libfontconfig
-sudo dpkg -i grafana_5.0.0_amd64.deb
+sudo dpkg -i grafana_5.0.3_amd64.deb
 ```
 ```
 
 
 ## APT Repository
 ## APT Repository

+ 1 - 1
docs/sources/installation/docker.md

@@ -83,7 +83,7 @@ $ docker run \
   -d \
   -d \
   -p 3000:3000 \
   -p 3000:3000 \
   --name grafana \
   --name grafana \
-  grafana/grafana:4.5.2
+  grafana/grafana:5.0.2
 ```
 ```
 
 
 ## Configuring AWS Credentials for CloudWatch Support
 ## Configuring AWS Credentials for CloudWatch Support

+ 5 - 5
docs/sources/installation/rpm.md

@@ -15,7 +15,7 @@ weight = 2
 
 
 Description | Download
 Description | Download
 ------------ | -------------
 ------------ | -------------
-Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.0 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-1.x86_64.rpm)
+Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.3 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.3-1.x86_64.rpm)
 
 
 
 
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
@@ -26,7 +26,7 @@ installation.
 You can install Grafana using Yum directly.
 You can install Grafana using Yum directly.
 
 
 ```bash
 ```bash
-$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-1.x86_64.rpm
+$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.3-1.x86_64.rpm
 ```
 ```
 
 
 Or install manually using `rpm`.
 Or install manually using `rpm`.
@@ -34,15 +34,15 @@ Or install manually using `rpm`.
 #### On CentOS / Fedora / Redhat:
 #### On CentOS / Fedora / Redhat:
 
 
 ```bash
 ```bash
-$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-1.x86_64.rpm
+$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.3-1.x86_64.rpm
 $ sudo yum install initscripts fontconfig
 $ sudo yum install initscripts fontconfig
-$ sudo rpm -Uvh grafana-5.0.0-1.x86_64.rpm
+$ sudo rpm -Uvh grafana-5.0.3-1.x86_64.rpm
 ```
 ```
 
 
 #### On OpenSuse:
 #### On OpenSuse:
 
 
 ```bash
 ```bash
-$ sudo rpm -i --nodeps grafana-5.0.0-1.x86_64.rpm
+$ sudo rpm -i --nodeps grafana-5.0.3-1.x86_64.rpm
 ```
 ```
 
 
 ## Install via YUM Repository
 ## Install via YUM Repository

+ 1 - 1
docs/sources/installation/windows.md

@@ -13,7 +13,7 @@ weight = 3
 
 
 Description | Download
 Description | Download
 ------------ | -------------
 ------------ | -------------
-Latest stable package for Windows | [grafana-5.0.0.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0.windows-x64.zip)
+Latest stable package for Windows | [grafana-5.0.3.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.3.windows-x64.zip)
 
 
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 installation.
 installation.

+ 68 - 1
docs/sources/reference/templating.md

@@ -1,6 +1,6 @@
 +++
 +++
 title = "Variables"
 title = "Variables"
-keywords = ["grafana", "templating", "documentation", "guide"]
+keywords = ["grafana", "templating", "documentation", "guide", "template", "variable"]
 type = "docs"
 type = "docs"
 [menu.docs]
 [menu.docs]
 name = "Variables"
 name = "Variables"
@@ -80,6 +80,73 @@ Option | Description
 *Regex* | Regex to filter or capture specific parts of the names return by your data source query. Optional.
 *Regex* | Regex to filter or capture specific parts of the names return by your data source query. Optional.
 *Sort* | Define sort order for options in dropdown. **Disabled** means that the order of options returned by your data source query will be used.
 *Sort* | Define sort order for options in dropdown. **Disabled** means that the order of options returned by your data source query will be used.
 
 
+#### Using regex to filter/modify values in the Variable dropdown
+
+Using the Regex Query Option, you filter the list of options returned by the Variable query or modify the options returned.
+
+Examples of filtering on the following list of options:
+
+```text
+backend_01
+backend_02
+backend_03
+backend_04
+```
+
+##### Filter so that only the options that end with `01` or `02` are returned:
+
+Regex:
+
+```regex
+/.*[01|02]/
+```
+
+Result:
+
+```text
+backend_01
+backend_02
+```
+
+##### Filter and modify the options using a regex capture group to return part of the text:
+
+Regex:
+
+```regex
+/.*(01|02)/
+```
+
+Result:
+
+```text
+01
+02
+```
+
+#### Filter and modify - Prometheus Example
+
+List of options:
+
+```text
+up{instance="demo.robustperception.io:9090",job="prometheus"} 1 1521630638000
+up{instance="demo.robustperception.io:9093",job="alertmanager"} 1 1521630638000
+up{instance="demo.robustperception.io:9100",job="node"} 1 1521630638000
+```
+
+Regex:
+
+```regex
+/.*instance="([^"]*).*/
+```
+
+Result:
+
+```text
+demo.robustperception.io:9090
+demo.robustperception.io:9093
+demo.robustperception.io:9100
+```
+
 ### Query expressions
 ### Query expressions
 
 
 The query expressions are different for each data source.
 The query expressions are different for each data source.

+ 22 - 14
docs/sources/tutorials/ha_setup.md

@@ -9,30 +9,38 @@ weight = 10
 
 
 # How to setup Grafana for high availability
 # How to setup Grafana for high availability
 
 
-> Alerting does not support high availability yet.
-
 Setting up Grafana for high availability is fairly simple. It comes down to two things:
 Setting up Grafana for high availability is fairly simple. It comes down to two things:
 
 
-  * Use a shared database for multiple grafana instances.
-  * Consider how user sessions are stored.
+  1. Use a shared database for storing dashboard, users, and other persistent data
+  2. Decide how to store session data.
+
+<div class="text-center">
+  <img src="/img/docs/tutorials/grafana-high-availability.png"  max-width= "800px" class="center"></img>
+</div>
 
 
 ## Configure multiple servers to use the same database
 ## Configure multiple servers to use the same database
 
 
-First you need to do is to setup mysql or postgres on another server and configure Grafana to use that database.
+First, you need to do is to setup MySQL or Postgres on another server and configure Grafana to use that database.
 You can find the configuration for doing that in the [[database]]({{< relref "configuration.md" >}}#database) section in the grafana config.
 You can find the configuration for doing that in the [[database]]({{< relref "configuration.md" >}}#database) section in the grafana config.
-Grafana will now persist all long term data in the database. 
-It also worth considering how to setup the database for high availability but thats outside the scope of this guide.
+Grafana will now persist all long term data in the database. How to configure the database for high availability is out of scope for this guide. We recommend finding an expert on for the database your using.
 
 
 ## User sessions
 ## User sessions
 
 
-The second thing to consider is how to deal with user sessions and how to balance the load between servers. 
-By default Grafana stores user sessions on disk which works fine if you use `sticky sessions` in your load balancer. 
-Grafana also supports storing the session data in the database, redis or memcache which makes it possible to use round robin in your load balancer. 
-If you use mysql/postgres for session storage you first need a table to store the session data in. More details about that in [[sessions]]({{< relref "configuration.md" >}}#session) 
+The second thing to consider is how to deal with user sessions and how to configure your load balancer infront of Grafana.
+Grafana support two says of storing session data locally on disk or in a database/cache-server.
+If you want to store sessions on disk you can use `sticky sessions` in your load balanacer. If you prefer to store session data in a database/cache-server
+you can use any stateless routing strategy in your load balancer (ex round robin or least connections).
+
+### Sticky sessions
+Using sticky sessions, all traffic for one user will always be sent to the same server. Which means that session related data can be
+stored on disk rather than on a shared database. This is the default behavior for Grafana and if only want multiple servers for fail over this is a good solution since it requires the least amount of work.
+
+### Stateless sessions
+You can also choose to store session data in a Redis/Memcache/Postgres/MySQL which means that the load balancer can send a user to any Grafana server without having to log in on each server. This requires a little bit more work from the operator but enables you to remove/add grafana servers without impacting the user experience.
+If you use MySQL/Postgres for session storage, you first need a table to store the session data in. More details about that in [[sessions]]({{< relref "configuration.md" >}}#session)
 
 
-For Grafana itself it doesn't really matter if you store your sessions on disk or database/redis/memcache.
-But we suggest that you store the session in redis/memcache since it makes it easier to add/remote instances from the group. 
+For Grafana itself it doesn't really matter if you store the session data on disk or database/redis/memcache. But we recommend using a database/redis/memcache since it makes it easier manage the grafana servers.
 
 
 ## Alerting
 ## Alerting
 
 
-Currently alerting supports a limited form of high availability. Since v4.2.0 of Grafana, alert notifications are deduped when running multiple servers. This means all alerts are executed on every server but no duplicate alert notifications are sent due to the deduping logic. Proper load balancing of alerts will be introduced in the future. 
+Currently alerting supports a limited form of high availability. Since v4.2.0, alert notifications are deduped when running multiple servers. This means all alerts are executed on every server but alert notifications are only sent once per alert. Grafana does not support distributing the alert rule execution between servers. That might be added in the future but right now prefer to keep it simple.

+ 1 - 1
emails/templates/layouts/default.html

@@ -143,7 +143,7 @@ td[class="stack-column-center"] {
 											<center>
 											<center>
 												<p style="text-align: center; font-size: 12px; color: #999999;">
 												<p style="text-align: center; font-size: 12px; color: #999999;">
 													Sent by <a href="[[.AppUrl]]">Grafana v[[.BuildVersion]]</a>
 													Sent by <a href="[[.AppUrl]]">Grafana v[[.BuildVersion]]</a>
-													<br />&copy; 2016 Grafana and raintank
+													<br />&copy; 2018 Grafana Labs
 												</p>
 												</p>
 											</center>
 											</center>
 										</td>
 										</td>

+ 2 - 2
package.json

@@ -4,7 +4,7 @@
     "company": "Grafana Labs"
     "company": "Grafana Labs"
   },
   },
   "name": "grafana",
   "name": "grafana",
-  "version": "5.0.1-pre1",
+  "version": "5.1.0-pre1",
   "repository": {
   "repository": {
     "type": "git",
     "type": "git",
     "url": "http://github.com/grafana/grafana.git"
     "url": "http://github.com/grafana/grafana.git"
@@ -118,7 +118,7 @@
       "prettier --write",
       "prettier --write",
       "git add"
       "git add"
     ],
     ],
-    "*.go": [
+    "*pkg/**/*.go": [
       "gofmt -w -s",
       "gofmt -w -s",
       "git add"
       "git add"
     ]
     ]

+ 1 - 1
packaging/publish/publish_both.sh

@@ -1,5 +1,5 @@
 #! /usr/bin/env bash
 #! /usr/bin/env bash
-version=4.6.3
+version=5.0.2
 
 
 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${version}_amd64.deb
 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${version}_amd64.deb
 
 

+ 11 - 3
pkg/api/admin.go

@@ -1,15 +1,15 @@
 package api
 package api
 
 
 import (
 import (
+	"regexp"
 	"strings"
 	"strings"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
-func AdminGetSettings(c *middleware.Context) {
+func AdminGetSettings(c *m.ReqContext) {
 	settings := make(map[string]interface{})
 	settings := make(map[string]interface{})
 
 
 	for _, section := range setting.Cfg.Sections() {
 	for _, section := range setting.Cfg.Sections() {
@@ -22,6 +22,14 @@ func AdminGetSettings(c *middleware.Context) {
 			if strings.Contains(keyName, "secret") || strings.Contains(keyName, "password") || (strings.Contains(keyName, "provider_config")) {
 			if strings.Contains(keyName, "secret") || strings.Contains(keyName, "password") || (strings.Contains(keyName, "provider_config")) {
 				value = "************"
 				value = "************"
 			}
 			}
+			if strings.Contains(keyName, "url") {
+				var rgx = regexp.MustCompile(`.*:\/\/([^:]*):([^@]*)@.*?$`)
+				var subs = rgx.FindAllSubmatch([]byte(value), -1)
+				if subs != nil && len(subs[0]) == 3 {
+					value = strings.Replace(value, string(subs[0][1]), "******", 1)
+					value = strings.Replace(value, string(subs[0][2]), "******", 1)
+				}
+			}
 
 
 			jsonSec[keyName] = value
 			jsonSec[keyName] = value
 		}
 		}
@@ -30,7 +38,7 @@ func AdminGetSettings(c *middleware.Context) {
 	c.JSON(200, settings)
 	c.JSON(200, settings)
 }
 }
 
 
-func AdminGetStats(c *middleware.Context) {
+func AdminGetStats(c *m.ReqContext) {
 
 
 	statsQuery := m.GetAdminStatsQuery{}
 	statsQuery := m.GetAdminStatsQuery{}
 
 

+ 11 - 12
pkg/api/admin_users.go

@@ -4,12 +4,11 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) {
+func AdminCreateUser(c *m.ReqContext, form dtos.AdminCreateUserForm) {
 	cmd := m.CreateUserCommand{
 	cmd := m.CreateUserCommand{
 		Login:    form.Login,
 		Login:    form.Login,
 		Email:    form.Email,
 		Email:    form.Email,
@@ -47,15 +46,15 @@ func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) {
 	c.JSON(200, result)
 	c.JSON(200, result)
 }
 }
 
 
-func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPasswordForm) {
-	userId := c.ParamsInt64(":id")
+func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordForm) {
+	userID := c.ParamsInt64(":id")
 
 
 	if len(form.Password) < 4 {
 	if len(form.Password) < 4 {
 		c.JsonApiErr(400, "New password too short", nil)
 		c.JsonApiErr(400, "New password too short", nil)
 		return
 		return
 	}
 	}
 
 
-	userQuery := m.GetUserByIdQuery{Id: userId}
+	userQuery := m.GetUserByIdQuery{Id: userID}
 
 
 	if err := bus.Dispatch(&userQuery); err != nil {
 	if err := bus.Dispatch(&userQuery); err != nil {
 		c.JsonApiErr(500, "Could not read user from database", err)
 		c.JsonApiErr(500, "Could not read user from database", err)
@@ -65,7 +64,7 @@ func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPas
 	passwordHashed := util.EncodePassword(form.Password, userQuery.Result.Salt)
 	passwordHashed := util.EncodePassword(form.Password, userQuery.Result.Salt)
 
 
 	cmd := m.ChangeUserPasswordCommand{
 	cmd := m.ChangeUserPasswordCommand{
-		UserId:      userId,
+		UserId:      userID,
 		NewPassword: passwordHashed,
 		NewPassword: passwordHashed,
 	}
 	}
 
 
@@ -77,11 +76,11 @@ func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPas
 	c.JsonOK("User password updated")
 	c.JsonOK("User password updated")
 }
 }
 
 
-func AdminUpdateUserPermissions(c *middleware.Context, form dtos.AdminUpdateUserPermissionsForm) {
-	userId := c.ParamsInt64(":id")
+func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermissionsForm) {
+	userID := c.ParamsInt64(":id")
 
 
 	cmd := m.UpdateUserPermissionsCommand{
 	cmd := m.UpdateUserPermissionsCommand{
-		UserId:         userId,
+		UserId:         userID,
 		IsGrafanaAdmin: form.IsGrafanaAdmin,
 		IsGrafanaAdmin: form.IsGrafanaAdmin,
 	}
 	}
 
 
@@ -93,10 +92,10 @@ func AdminUpdateUserPermissions(c *middleware.Context, form dtos.AdminUpdateUser
 	c.JsonOK("User permissions updated")
 	c.JsonOK("User permissions updated")
 }
 }
 
 
-func AdminDeleteUser(c *middleware.Context) {
-	userId := c.ParamsInt64(":id")
+func AdminDeleteUser(c *m.ReqContext) {
+	userID := c.ParamsInt64(":id")
 
 
-	cmd := m.DeleteUserCommand{UserId: userId}
+	cmd := m.DeleteUserCommand{UserId: userID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		c.JsonApiErr(500, "Failed to delete user", err)
 		c.JsonApiErr(500, "Failed to delete user", err)

+ 33 - 34
pkg/api/alerting.go

@@ -5,15 +5,14 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/models"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/alerting"
 	"github.com/grafana/grafana/pkg/services/alerting"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 )
 )
 
 
-func ValidateOrgAlert(c *middleware.Context) {
+func ValidateOrgAlert(c *m.ReqContext) {
 	id := c.ParamsInt64(":alertId")
 	id := c.ParamsInt64(":alertId")
-	query := models.GetAlertByIdQuery{Id: id}
+	query := m.GetAlertByIdQuery{Id: id}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		c.JsonApiErr(404, "Alert not found", nil)
 		c.JsonApiErr(404, "Alert not found", nil)
@@ -26,14 +25,14 @@ func ValidateOrgAlert(c *middleware.Context) {
 	}
 	}
 }
 }
 
 
-func GetAlertStatesForDashboard(c *middleware.Context) Response {
-	dashboardId := c.QueryInt64("dashboardId")
+func GetAlertStatesForDashboard(c *m.ReqContext) Response {
+	dashboardID := c.QueryInt64("dashboardId")
 
 
-	if dashboardId == 0 {
+	if dashboardID == 0 {
 		return ApiError(400, "Missing query parameter dashboardId", nil)
 		return ApiError(400, "Missing query parameter dashboardId", nil)
 	}
 	}
 
 
-	query := models.GetAlertStatesForDashboardQuery{
+	query := m.GetAlertStatesForDashboardQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
 		DashboardId: c.QueryInt64("dashboardId"),
 		DashboardId: c.QueryInt64("dashboardId"),
 	}
 	}
@@ -46,8 +45,8 @@ func GetAlertStatesForDashboard(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/alerts
 // GET /api/alerts
-func GetAlerts(c *middleware.Context) Response {
-	query := models.GetAlertsQuery{
+func GetAlerts(c *m.ReqContext) Response {
+	query := m.GetAlertsQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
 		DashboardId: c.QueryInt64("dashboardId"),
 		DashboardId: c.QueryInt64("dashboardId"),
 		PanelId:     c.QueryInt64("panelId"),
 		PanelId:     c.QueryInt64("panelId"),
@@ -65,14 +64,14 @@ func GetAlerts(c *middleware.Context) Response {
 	}
 	}
 
 
 	for _, alert := range query.Result {
 	for _, alert := range query.Result {
-		alert.Url = models.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
+		alert.Url = m.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
 	}
 	}
 
 
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
 // POST /api/alerts/test
 // POST /api/alerts/test
-func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
+func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response {
 	if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
 	if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
 		return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
 		return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
 	}
 	}
@@ -114,9 +113,9 @@ func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
 }
 }
 
 
 // GET /api/alerts/:id
 // GET /api/alerts/:id
-func GetAlert(c *middleware.Context) Response {
+func GetAlert(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":alertId")
 	id := c.ParamsInt64(":alertId")
-	query := models.GetAlertByIdQuery{Id: id}
+	query := m.GetAlertByIdQuery{Id: id}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return ApiError(500, "List alerts failed", err)
 		return ApiError(500, "List alerts failed", err)
@@ -125,12 +124,12 @@ func GetAlert(c *middleware.Context) Response {
 	return Json(200, &query.Result)
 	return Json(200, &query.Result)
 }
 }
 
 
-func GetAlertNotifiers(c *middleware.Context) Response {
+func GetAlertNotifiers(c *m.ReqContext) Response {
 	return Json(200, alerting.GetNotifiers())
 	return Json(200, alerting.GetNotifiers())
 }
 }
 
 
-func GetAlertNotifications(c *middleware.Context) Response {
-	query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId}
+func GetAlertNotifications(c *m.ReqContext) Response {
+	query := &m.GetAllAlertNotificationsQuery{OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(query); err != nil {
 	if err := bus.Dispatch(query); err != nil {
 		return ApiError(500, "Failed to get alert notifications", err)
 		return ApiError(500, "Failed to get alert notifications", err)
@@ -152,8 +151,8 @@ func GetAlertNotifications(c *middleware.Context) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetAlertNotificationById(c *middleware.Context) Response {
-	query := &models.GetAlertNotificationsQuery{
+func GetAlertNotificationByID(c *m.ReqContext) Response {
+	query := &m.GetAlertNotificationsQuery{
 		OrgId: c.OrgId,
 		OrgId: c.OrgId,
 		Id:    c.ParamsInt64("notificationId"),
 		Id:    c.ParamsInt64("notificationId"),
 	}
 	}
@@ -165,7 +164,7 @@ func GetAlertNotificationById(c *middleware.Context) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotificationCommand) Response {
+func CreateAlertNotification(c *m.ReqContext, cmd m.CreateAlertNotificationCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
@@ -175,7 +174,7 @@ func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotifi
 	return Json(200, cmd.Result)
 	return Json(200, cmd.Result)
 }
 }
 
 
-func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotificationCommand) Response {
+func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
@@ -185,8 +184,8 @@ func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotifi
 	return Json(200, cmd.Result)
 	return Json(200, cmd.Result)
 }
 }
 
 
-func DeleteAlertNotification(c *middleware.Context) Response {
-	cmd := models.DeleteAlertNotificationCommand{
+func DeleteAlertNotification(c *m.ReqContext) Response {
+	cmd := m.DeleteAlertNotificationCommand{
 		OrgId: c.OrgId,
 		OrgId: c.OrgId,
 		Id:    c.ParamsInt64("notificationId"),
 		Id:    c.ParamsInt64("notificationId"),
 	}
 	}
@@ -199,7 +198,7 @@ func DeleteAlertNotification(c *middleware.Context) Response {
 }
 }
 
 
 //POST /api/alert-notifications/test
 //POST /api/alert-notifications/test
-func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
+func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Response {
 	cmd := &alerting.NotificationTestCommand{
 	cmd := &alerting.NotificationTestCommand{
 		Name:     dto.Name,
 		Name:     dto.Name,
 		Type:     dto.Type,
 		Type:     dto.Type,
@@ -207,7 +206,7 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
 	}
 	}
 
 
 	if err := bus.Dispatch(cmd); err != nil {
 	if err := bus.Dispatch(cmd); err != nil {
-		if err == models.ErrSmtpNotEnabled {
+		if err == m.ErrSmtpNotEnabled {
 			return ApiError(412, err.Error(), err)
 			return ApiError(412, err.Error(), err)
 		}
 		}
 		return ApiError(500, "Failed to send alert notifications", err)
 		return ApiError(500, "Failed to send alert notifications", err)
@@ -217,10 +216,10 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
 }
 }
 
 
 //POST /api/alerts/:alertId/pause
 //POST /api/alerts/:alertId/pause
-func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
+func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response {
 	alertId := c.ParamsInt64("alertId")
 	alertId := c.ParamsInt64("alertId")
 
 
-	query := models.GetAlertByIdQuery{Id: alertId}
+	query := m.GetAlertByIdQuery{Id: alertId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return ApiError(500, "Get Alert failed", err)
 		return ApiError(500, "Get Alert failed", err)
@@ -235,7 +234,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 		return ApiError(403, "Access denied to this dashboard and alert", nil)
 		return ApiError(403, "Access denied to this dashboard and alert", nil)
 	}
 	}
 
 
-	cmd := models.PauseAlertCommand{
+	cmd := m.PauseAlertCommand{
 		OrgId:    c.OrgId,
 		OrgId:    c.OrgId,
 		AlertIds: []int64{alertId},
 		AlertIds: []int64{alertId},
 		Paused:   dto.Paused,
 		Paused:   dto.Paused,
@@ -245,10 +244,10 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 		return ApiError(500, "", err)
 		return ApiError(500, "", err)
 	}
 	}
 
 
-	var response models.AlertStateType = models.AlertStatePending
+	var response m.AlertStateType = m.AlertStatePending
 	pausedState := "un-paused"
 	pausedState := "un-paused"
 	if cmd.Paused {
 	if cmd.Paused {
-		response = models.AlertStatePaused
+		response = m.AlertStatePaused
 		pausedState = "paused"
 		pausedState = "paused"
 	}
 	}
 
 
@@ -262,8 +261,8 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 }
 }
 
 
 //POST /api/admin/pause-all-alerts
 //POST /api/admin/pause-all-alerts
-func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response {
-	updateCmd := models.PauseAllAlertCommand{
+func PauseAllAlerts(c *m.ReqContext, dto dtos.PauseAllAlertsCommand) Response {
+	updateCmd := m.PauseAllAlertCommand{
 		Paused: dto.Paused,
 		Paused: dto.Paused,
 	}
 	}
 
 
@@ -271,10 +270,10 @@ func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Respo
 		return ApiError(500, "Failed to pause alerts", err)
 		return ApiError(500, "Failed to pause alerts", err)
 	}
 	}
 
 
-	var response models.AlertStateType = models.AlertStatePending
+	var response m.AlertStateType = m.AlertStatePending
 	pausedState := "un paused"
 	pausedState := "un paused"
 	if updateCmd.Paused {
 	if updateCmd.Paused {
-		response = models.AlertStatePaused
+		response = m.AlertStatePaused
 		pausedState = "paused"
 		pausedState = "paused"
 	}
 	}
 
 

+ 1 - 2
pkg/api/alerting_test.go

@@ -5,7 +5,6 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 
 
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
@@ -81,7 +80,7 @@ func postAlertScenario(desc string, url string, routePattern string, role m.Role
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID

+ 25 - 26
pkg/api/annotations.go

@@ -6,14 +6,13 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/annotations"
 	"github.com/grafana/grafana/pkg/services/annotations"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func GetAnnotations(c *middleware.Context) Response {
+func GetAnnotations(c *m.ReqContext) Response {
 
 
 	query := &annotations.ItemQuery{
 	query := &annotations.ItemQuery{
 		From:        c.QueryInt64("from") / 1000,
 		From:        c.QueryInt64("from") / 1000,
@@ -52,8 +51,8 @@ func (e *CreateAnnotationError) Error() string {
 	return e.message
 	return e.message
 }
 }
 
 
-func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response {
-	if canSave, err := canSaveByDashboardId(c, cmd.DashboardId); err != nil || !canSave {
+func PostAnnotation(c *m.ReqContext, cmd dtos.PostAnnotationsCmd) Response {
+	if canSave, err := canSaveByDashboardID(c, cmd.DashboardId); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
@@ -125,7 +124,7 @@ func formatGraphiteAnnotation(what string, data string) string {
 	return text
 	return text
 }
 }
 
 
-func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotationsCmd) Response {
+func PostGraphiteAnnotation(c *m.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd) Response {
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
 
 
 	if cmd.What == "" {
 	if cmd.What == "" {
@@ -179,19 +178,19 @@ func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotati
 	})
 	})
 }
 }
 
 
-func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Response {
-	annotationId := c.ParamsInt64(":annotationId")
+func UpdateAnnotation(c *m.ReqContext, cmd dtos.UpdateAnnotationsCmd) Response {
+	annotationID := c.ParamsInt64(":annotationId")
 
 
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
 
 
-	if resp := canSave(c, repo, annotationId); resp != nil {
+	if resp := canSave(c, repo, annotationID); resp != nil {
 		return resp
 		return resp
 	}
 	}
 
 
 	item := annotations.Item{
 	item := annotations.Item{
 		OrgId:  c.OrgId,
 		OrgId:  c.OrgId,
 		UserId: c.UserId,
 		UserId: c.UserId,
-		Id:     annotationId,
+		Id:     annotationID,
 		Epoch:  cmd.Time / 1000,
 		Epoch:  cmd.Time / 1000,
 		Text:   cmd.Text,
 		Text:   cmd.Text,
 		Tags:   cmd.Tags,
 		Tags:   cmd.Tags,
@@ -218,7 +217,7 @@ func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Resp
 	return ApiSuccess("Annotation updated")
 	return ApiSuccess("Annotation updated")
 }
 }
 
 
-func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Response {
+func DeleteAnnotations(c *m.ReqContext, cmd dtos.DeleteAnnotationsCmd) Response {
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
 
 
 	err := repo.Delete(&annotations.DeleteParams{
 	err := repo.Delete(&annotations.DeleteParams{
@@ -234,7 +233,7 @@ func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Res
 	return ApiSuccess("Annotations deleted")
 	return ApiSuccess("Annotations deleted")
 }
 }
 
 
-func DeleteAnnotationById(c *middleware.Context) Response {
+func DeleteAnnotationById(c *m.ReqContext) Response {
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
 	annotationId := c.ParamsInt64(":annotationId")
 	annotationId := c.ParamsInt64(":annotationId")
 
 
@@ -253,16 +252,16 @@ func DeleteAnnotationById(c *middleware.Context) Response {
 	return ApiSuccess("Annotation deleted")
 	return ApiSuccess("Annotation deleted")
 }
 }
 
 
-func DeleteAnnotationRegion(c *middleware.Context) Response {
+func DeleteAnnotationRegion(c *m.ReqContext) Response {
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
-	regionId := c.ParamsInt64(":regionId")
+	regionID := c.ParamsInt64(":regionId")
 
 
-	if resp := canSave(c, repo, regionId); resp != nil {
+	if resp := canSave(c, repo, regionID); resp != nil {
 		return resp
 		return resp
 	}
 	}
 
 
 	err := repo.Delete(&annotations.DeleteParams{
 	err := repo.Delete(&annotations.DeleteParams{
-		RegionId: regionId,
+		RegionId: regionID,
 	})
 	})
 
 
 	if err != nil {
 	if err != nil {
@@ -272,13 +271,13 @@ func DeleteAnnotationRegion(c *middleware.Context) Response {
 	return ApiSuccess("Annotation region deleted")
 	return ApiSuccess("Annotation region deleted")
 }
 }
 
 
-func canSaveByDashboardId(c *middleware.Context, dashboardId int64) (bool, error) {
-	if dashboardId == 0 && !c.SignedInUser.HasRole(m.ROLE_EDITOR) {
+func canSaveByDashboardID(c *m.ReqContext, dashboardID int64) (bool, error) {
+	if dashboardID == 0 && !c.SignedInUser.HasRole(m.ROLE_EDITOR) {
 		return false, nil
 		return false, nil
 	}
 	}
 
 
-	if dashboardId > 0 {
-		guardian := guardian.New(dashboardId, c.OrgId, c.SignedInUser)
+	if dashboardID > 0 {
+		guardian := guardian.New(dashboardID, c.OrgId, c.SignedInUser)
 		if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
 		if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
 			return false, err
 			return false, err
 		}
 		}
@@ -287,32 +286,32 @@ func canSaveByDashboardId(c *middleware.Context, dashboardId int64) (bool, error
 	return true, nil
 	return true, nil
 }
 }
 
 
-func canSave(c *middleware.Context, repo annotations.Repository, annotationId int64) Response {
+func canSave(c *m.ReqContext, repo annotations.Repository, annotationId int64) Response {
 	items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationId, OrgId: c.OrgId})
 	items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationId, OrgId: c.OrgId})
 
 
 	if err != nil || len(items) == 0 {
 	if err != nil || len(items) == 0 {
 		return ApiError(500, "Could not find annotation to update", err)
 		return ApiError(500, "Could not find annotation to update", err)
 	}
 	}
 
 
-	dashboardId := items[0].DashboardId
+	dashboardID := items[0].DashboardId
 
 
-	if canSave, err := canSaveByDashboardId(c, dashboardId); err != nil || !canSave {
+	if canSave, err := canSaveByDashboardID(c, dashboardID); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func canSaveByRegionId(c *middleware.Context, repo annotations.Repository, regionId int64) Response {
-	items, err := repo.Find(&annotations.ItemQuery{RegionId: regionId, OrgId: c.OrgId})
+func canSaveByRegionID(c *m.ReqContext, repo annotations.Repository, regionID int64) Response {
+	items, err := repo.Find(&annotations.ItemQuery{RegionId: regionID, OrgId: c.OrgId})
 
 
 	if err != nil || len(items) == 0 {
 	if err != nil || len(items) == 0 {
 		return ApiError(500, "Could not find annotation to update", err)
 		return ApiError(500, "Could not find annotation to update", err)
 	}
 	}
 
 
-	dashboardId := items[0].DashboardId
+	dashboardID := items[0].DashboardId
 
 
-	if canSave, err := canSaveByDashboardId(c, dashboardId); err != nil || !canSave {
+	if canSave, err := canSaveByDashboardID(c, dashboardID); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 

+ 2 - 3
pkg/api/annotations_test.go

@@ -5,7 +5,6 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/annotations"
 	"github.com/grafana/grafana/pkg/services/annotations"
 
 
@@ -200,7 +199,7 @@ func postAnnotationScenario(desc string, url string, routePattern string, role m
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID
@@ -223,7 +222,7 @@ func putAnnotationScenario(desc string, url string, routePattern string, role m.
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID

+ 22 - 21
pkg/api/api.go

@@ -15,7 +15,7 @@ func (hs *HttpServer) registerRoutes() {
 	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
 	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
 	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
 	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
 	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
 	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
-	redirectFromLegacyDashboardUrl := middleware.RedirectFromLegacyDashboardUrl()
+	redirectFromLegacyDashboardUrl := middleware.RedirectFromLegacyDashboardURL()
 	redirectFromLegacyDashboardSoloUrl := middleware.RedirectFromLegacyDashboardSoloUrl()
 	redirectFromLegacyDashboardSoloUrl := middleware.RedirectFromLegacyDashboardSoloUrl()
 	quota := middleware.Quota
 	quota := middleware.Quota
 	bind := binding.Bind
 	bind := binding.Bind
@@ -66,6 +66,7 @@ func (hs *HttpServer) registerRoutes() {
 	r.Get("/plugins/:id/page/:page", reqSignedIn, Index)
 	r.Get("/plugins/:id/page/:page", reqSignedIn, Index)
 
 
 	r.Get("/d/:uid/:slug", reqSignedIn, Index)
 	r.Get("/d/:uid/:slug", reqSignedIn, Index)
+	r.Get("/d/:uid", reqSignedIn, Index)
 	r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardUrl, Index)
 	r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardUrl, Index)
 	r.Get("/dashboard/script/*", reqSignedIn, Index)
 	r.Get("/dashboard/script/*", reqSignedIn, Index)
 	r.Get("/dashboard-solo/snapshot/*", Index)
 	r.Get("/dashboard-solo/snapshot/*", Index)
@@ -109,7 +110,7 @@ func (hs *HttpServer) registerRoutes() {
 	r.Get("/api/snapshots-delete/:key", reqEditorRole, wrap(DeleteDashboardSnapshot))
 	r.Get("/api/snapshots-delete/:key", reqEditorRole, wrap(DeleteDashboardSnapshot))
 
 
 	// api renew session based on remember cookie
 	// api renew session based on remember cookie
-	r.Get("/api/login/ping", quota("session"), LoginApiPing)
+	r.Get("/api/login/ping", quota("session"), LoginAPIPing)
 
 
 	// authed api
 	// authed api
 	r.Group("/api", func(apiRoute RouteRegister) {
 	r.Group("/api", func(apiRoute RouteRegister) {
@@ -138,7 +139,7 @@ func (hs *HttpServer) registerRoutes() {
 		apiRoute.Group("/users", func(usersRoute RouteRegister) {
 		apiRoute.Group("/users", func(usersRoute RouteRegister) {
 			usersRoute.Get("/", wrap(SearchUsers))
 			usersRoute.Get("/", wrap(SearchUsers))
 			usersRoute.Get("/search", wrap(SearchUsersWithPaging))
 			usersRoute.Get("/search", wrap(SearchUsersWithPaging))
-			usersRoute.Get("/:id", wrap(GetUserById))
+			usersRoute.Get("/:id", wrap(GetUserByID))
 			usersRoute.Get("/:id/orgs", wrap(GetUserOrgList))
 			usersRoute.Get("/:id/orgs", wrap(GetUserOrgList))
 			// query parameters /users/lookup?loginOrEmail=admin@example.com
 			// query parameters /users/lookup?loginOrEmail=admin@example.com
 			usersRoute.Get("/lookup", wrap(GetUserByLoginOrEmail))
 			usersRoute.Get("/lookup", wrap(GetUserByLoginOrEmail))
@@ -148,13 +149,13 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// team (admin permission required)
 		// team (admin permission required)
 		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
 		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
-			teamsRoute.Get("/:teamId", wrap(GetTeamById))
+			teamsRoute.Get("/:teamId", wrap(GetTeamByID))
 			teamsRoute.Get("/search", wrap(SearchTeams))
 			teamsRoute.Get("/search", wrap(SearchTeams))
-			teamsRoute.Post("/", quota("teams"), bind(m.CreateTeamCommand{}), wrap(CreateTeam))
+			teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
 			teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
 			teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
-			teamsRoute.Delete("/:teamId", wrap(DeleteTeamById))
+			teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID))
 			teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
 			teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
-			teamsRoute.Post("/:teamId/members", quota("teams"), bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
+			teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
 			teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
 			teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
@@ -191,10 +192,10 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// orgs (admin routes)
 		// orgs (admin routes)
 		apiRoute.Group("/orgs/:orgId", func(orgsRoute RouteRegister) {
 		apiRoute.Group("/orgs/:orgId", func(orgsRoute RouteRegister) {
-			orgsRoute.Get("/", wrap(GetOrgById))
+			orgsRoute.Get("/", wrap(GetOrgByID))
 			orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
 			orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
 			orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
 			orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
-			orgsRoute.Delete("/", wrap(DeleteOrgById))
+			orgsRoute.Delete("/", wrap(DeleteOrgByID))
 			orgsRoute.Get("/users", wrap(GetOrgUsers))
 			orgsRoute.Get("/users", wrap(GetOrgUsers))
 			orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
 			orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
 			orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
 			orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
@@ -210,9 +211,9 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// auth api keys
 		// auth api keys
 		apiRoute.Group("/auth/keys", func(keysRoute RouteRegister) {
 		apiRoute.Group("/auth/keys", func(keysRoute RouteRegister) {
-			keysRoute.Get("/", wrap(GetApiKeys))
-			keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
-			keysRoute.Delete("/:id", wrap(DeleteApiKey))
+			keysRoute.Get("/", wrap(GetAPIKeys))
+			keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddAPIKey))
+			keysRoute.Delete("/:id", wrap(DeleteAPIKey))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
 		// Preferences
 		// Preferences
@@ -225,16 +226,16 @@ func (hs *HttpServer) registerRoutes() {
 			datasourceRoute.Get("/", wrap(GetDataSources))
 			datasourceRoute.Get("/", wrap(GetDataSources))
 			datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), wrap(AddDataSource))
 			datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), wrap(AddDataSource))
 			datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), wrap(UpdateDataSource))
 			datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), wrap(UpdateDataSource))
-			datasourceRoute.Delete("/:id", wrap(DeleteDataSourceById))
+			datasourceRoute.Delete("/:id", wrap(DeleteDataSourceByID))
 			datasourceRoute.Delete("/name/:name", wrap(DeleteDataSourceByName))
 			datasourceRoute.Delete("/name/:name", wrap(DeleteDataSourceByName))
-			datasourceRoute.Get("/:id", wrap(GetDataSourceById))
+			datasourceRoute.Get("/:id", wrap(GetDataSourceByID))
 			datasourceRoute.Get("/name/:name", wrap(GetDataSourceByName))
 			datasourceRoute.Get("/name/:name", wrap(GetDataSourceByName))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
-		apiRoute.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
+		apiRoute.Get("/datasources/id/:name", wrap(GetDataSourceIDByName), reqSignedIn)
 
 
 		apiRoute.Get("/plugins", wrap(GetPluginList))
 		apiRoute.Get("/plugins", wrap(GetPluginList))
-		apiRoute.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
+		apiRoute.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingByID))
 		apiRoute.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown))
 		apiRoute.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown))
 
 
 		apiRoute.Group("/plugins", func(pluginRoute RouteRegister) {
 		apiRoute.Group("/plugins", func(pluginRoute RouteRegister) {
@@ -249,11 +250,11 @@ func (hs *HttpServer) registerRoutes() {
 		// Folders
 		// Folders
 		apiRoute.Group("/folders", func(folderRoute RouteRegister) {
 		apiRoute.Group("/folders", func(folderRoute RouteRegister) {
 			folderRoute.Get("/", wrap(GetFolders))
 			folderRoute.Get("/", wrap(GetFolders))
-			folderRoute.Get("/id/:id", wrap(GetFolderById))
+			folderRoute.Get("/id/:id", wrap(GetFolderByID))
 			folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder))
 			folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder))
 
 
 			folderRoute.Group("/:uid", func(folderUidRoute RouteRegister) {
 			folderRoute.Group("/:uid", func(folderUidRoute RouteRegister) {
-				folderUidRoute.Get("/", wrap(GetFolderByUid))
+				folderUidRoute.Get("/", wrap(GetFolderByUID))
 				folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder))
 				folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder))
 				folderUidRoute.Delete("/", wrap(DeleteFolder))
 				folderUidRoute.Delete("/", wrap(DeleteFolder))
 
 
@@ -267,7 +268,7 @@ func (hs *HttpServer) registerRoutes() {
 		// Dashboard
 		// Dashboard
 		apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
 		apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
 			dashboardRoute.Get("/uid/:uid", wrap(GetDashboard))
 			dashboardRoute.Get("/uid/:uid", wrap(GetDashboard))
-			dashboardRoute.Delete("/uid/:uid", wrap(DeleteDashboardByUid))
+			dashboardRoute.Delete("/uid/:uid", wrap(DeleteDashboardByUID))
 
 
 			dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
 			dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
 			dashboardRoute.Delete("/db/:slug", wrap(DeleteDashboard))
 			dashboardRoute.Delete("/db/:slug", wrap(DeleteDashboard))
@@ -313,7 +314,7 @@ func (hs *HttpServer) registerRoutes() {
 		// metrics
 		// metrics
 		apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics))
 		apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics))
 		apiRoute.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios))
 		apiRoute.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios))
-		apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSqlTestData))
+		apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSQLTestData))
 		apiRoute.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk))
 		apiRoute.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk))
 
 
 		apiRoute.Group("/alerts", func(alertsRoute RouteRegister) {
 		apiRoute.Group("/alerts", func(alertsRoute RouteRegister) {
@@ -331,7 +332,7 @@ func (hs *HttpServer) registerRoutes() {
 			alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
 			alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
 			alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
 			alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
 			alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
 			alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
-			alertNotifications.Get("/:notificationId", wrap(GetAlertNotificationById))
+			alertNotifications.Get("/:notificationId", wrap(GetAlertNotificationByID))
 			alertNotifications.Delete("/:notificationId", wrap(DeleteAlertNotification))
 			alertNotifications.Delete("/:notificationId", wrap(DeleteAlertNotification))
 		}, reqEditorRole)
 		}, reqEditorRole)
 
 

+ 3 - 4
pkg/api/apikey.go

@@ -4,11 +4,10 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/apikeygen"
 	"github.com/grafana/grafana/pkg/components/apikeygen"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
-func GetApiKeys(c *middleware.Context) Response {
+func GetAPIKeys(c *m.ReqContext) Response {
 	query := m.GetApiKeysQuery{OrgId: c.OrgId}
 	query := m.GetApiKeysQuery{OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -27,7 +26,7 @@ func GetApiKeys(c *middleware.Context) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func DeleteApiKey(c *middleware.Context) Response {
+func DeleteAPIKey(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
 	cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
@@ -40,7 +39,7 @@ func DeleteApiKey(c *middleware.Context) Response {
 	return ApiSuccess("API key deleted")
 	return ApiSuccess("API key deleted")
 }
 }
 
 
-func AddApiKey(c *middleware.Context, cmd m.AddApiKeyCommand) Response {
+func AddAPIKey(c *m.ReqContext, cmd m.AddApiKeyCommand) Response {
 	if !cmd.Role.IsValid() {
 	if !cmd.Role.IsValid() {
 		return ApiError(400, "Invalid role specified", nil)
 		return ApiError(400, "Invalid role specified", nil)
 	}
 	}

+ 3 - 3
pkg/api/app_routes.go

@@ -55,11 +55,11 @@ func InitAppPluginRoutes(r *macaron.Macaron) {
 	}
 	}
 }
 }
 
 
-func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler {
-	return func(c *middleware.Context) {
+func AppPluginRoute(route *plugins.AppPluginRoute, appID string) macaron.Handler {
+	return func(c *m.ReqContext) {
 		path := c.Params("*")
 		path := c.Params("*")
 
 
-		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
+		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appID)
 		proxy.Transport = pluginProxyTransport
 		proxy.Transport = pluginProxyTransport
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 	}
 	}

+ 4 - 4
pkg/api/common.go

@@ -4,7 +4,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"net/http"
 	"net/http"
 
 
-	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 )
 )
@@ -19,7 +19,7 @@ var (
 )
 )
 
 
 type Response interface {
 type Response interface {
-	WriteTo(ctx *middleware.Context)
+	WriteTo(ctx *m.ReqContext)
 }
 }
 
 
 type NormalResponse struct {
 type NormalResponse struct {
@@ -32,7 +32,7 @@ type NormalResponse struct {
 
 
 func wrap(action interface{}) macaron.Handler {
 func wrap(action interface{}) macaron.Handler {
 
 
-	return func(c *middleware.Context) {
+	return func(c *m.ReqContext) {
 		var res Response
 		var res Response
 		val, err := c.Invoke(action)
 		val, err := c.Invoke(action)
 		if err == nil && val != nil && len(val) > 0 {
 		if err == nil && val != nil && len(val) > 0 {
@@ -45,7 +45,7 @@ func wrap(action interface{}) macaron.Handler {
 	}
 	}
 }
 }
 
 
-func (r *NormalResponse) WriteTo(ctx *middleware.Context) {
+func (r *NormalResponse) WriteTo(ctx *m.ReqContext) {
 	if r.err != nil {
 	if r.err != nil {
 		ctx.Logger.Error(r.errMessage, "error", r.err)
 		ctx.Logger.Error(r.errMessage, "error", r.err)
 	}
 	}

+ 8 - 8
pkg/api/common_test.go

@@ -8,22 +8,22 @@ import (
 	"github.com/go-macaron/session"
 	"github.com/go-macaron/session"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/models"
-	macaron "gopkg.in/macaron.v1"
+	m "github.com/grafana/grafana/pkg/models"
+	"gopkg.in/macaron.v1"
 
 
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
 )
 )
 
 
 func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
 func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
-	loggedInUserScenarioWithRole(desc, "GET", url, url, models.ROLE_EDITOR, fn)
+	loggedInUserScenarioWithRole(desc, "GET", url, url, m.ROLE_EDITOR, fn)
 }
 }
 
 
-func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
+func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role m.RoleType, fn scenarioFunc) {
 	Convey(desc+" "+url, func() {
 	Convey(desc+" "+url, func() {
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID
@@ -71,7 +71,7 @@ func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map
 
 
 type scenarioContext struct {
 type scenarioContext struct {
 	m              *macaron.Macaron
 	m              *macaron.Macaron
-	context        *middleware.Context
+	context        *m.ReqContext
 	resp           *httptest.ResponseRecorder
 	resp           *httptest.ResponseRecorder
 	handlerFunc    handlerFunc
 	handlerFunc    handlerFunc
 	defaultHandler macaron.Handler
 	defaultHandler macaron.Handler
@@ -84,7 +84,7 @@ func (sc *scenarioContext) exec() {
 }
 }
 
 
 type scenarioFunc func(c *scenarioContext)
 type scenarioFunc func(c *scenarioContext)
-type handlerFunc func(c *middleware.Context) Response
+type handlerFunc func(c *m.ReqContext) Response
 
 
 func setupScenarioContext(url string) *scenarioContext {
 func setupScenarioContext(url string) *scenarioContext {
 	sc := &scenarioContext{
 	sc := &scenarioContext{
@@ -99,7 +99,7 @@ func setupScenarioContext(url string) *scenarioContext {
 	}))
 	}))
 
 
 	sc.m.Use(middleware.GetContextHandler())
 	sc.m.Use(middleware.GetContextHandler())
-	sc.m.Use(middleware.Sessioner(&session.Options{}))
+	sc.m.Use(middleware.Sessioner(&session.Options{}, 0))
 
 
 	return sc
 	return sc
 }
 }

+ 29 - 32
pkg/api/dashboard.go

@@ -14,20 +14,20 @@ import (
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
+	"github.com/grafana/grafana/pkg/services/quota"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func isDashboardStarredByUser(c *middleware.Context, dashId int64) (bool, error) {
+func isDashboardStarredByUser(c *m.ReqContext, dashID int64) (bool, error) {
 	if !c.IsSignedIn {
 	if !c.IsSignedIn {
 		return false, nil
 		return false, nil
 	}
 	}
 
 
-	query := m.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashId}
+	query := m.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashID}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return false, err
 		return false, err
 	}
 	}
@@ -43,7 +43,7 @@ func dashboardGuardianResponse(err error) Response {
 	return ApiError(403, "Access denied to this dashboard", nil)
 	return ApiError(403, "Access denied to this dashboard", nil)
 }
 }
 
 
-func GetDashboard(c *middleware.Context) Response {
+func GetDashboard(c *m.ReqContext) Response {
 	dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
 	dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
@@ -114,24 +114,22 @@ func GetDashboard(c *middleware.Context) Response {
 	return Json(200, dto)
 	return Json(200, dto)
 }
 }
 
 
-func getUserLogin(userId int64) string {
-	query := m.GetUserByIdQuery{Id: userId}
+func getUserLogin(userID int64) string {
+	query := m.GetUserByIdQuery{Id: userID}
 	err := bus.Dispatch(&query)
 	err := bus.Dispatch(&query)
 	if err != nil {
 	if err != nil {
 		return "Anonymous"
 		return "Anonymous"
-	} else {
-		user := query.Result
-		return user.Login
 	}
 	}
+	return query.Result.Login
 }
 }
 
 
-func getDashboardHelper(orgId int64, slug string, id int64, uid string) (*m.Dashboard, Response) {
+func getDashboardHelper(orgID int64, slug string, id int64, uid string) (*m.Dashboard, Response) {
 	var query m.GetDashboardQuery
 	var query m.GetDashboardQuery
 
 
 	if len(uid) > 0 {
 	if len(uid) > 0 {
-		query = m.GetDashboardQuery{Uid: uid, Id: id, OrgId: orgId}
+		query = m.GetDashboardQuery{Uid: uid, Id: id, OrgId: orgID}
 	} else {
 	} else {
-		query = m.GetDashboardQuery{Slug: slug, Id: id, OrgId: orgId}
+		query = m.GetDashboardQuery{Slug: slug, Id: id, OrgId: orgID}
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -141,7 +139,7 @@ func getDashboardHelper(orgId int64, slug string, id int64, uid string) (*m.Dash
 	return query.Result, nil
 	return query.Result, nil
 }
 }
 
 
-func DeleteDashboard(c *middleware.Context) Response {
+func DeleteDashboard(c *m.ReqContext) Response {
 	query := m.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: c.Params(":slug")}
 	query := m.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: c.Params(":slug")}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -173,7 +171,7 @@ func DeleteDashboard(c *middleware.Context) Response {
 	})
 	})
 }
 }
 
 
-func DeleteDashboardByUid(c *middleware.Context) Response {
+func DeleteDashboardByUID(c *m.ReqContext) Response {
 	dash, rsp := getDashboardHelper(c.OrgId, "", 0, c.Params(":uid"))
 	dash, rsp := getDashboardHelper(c.OrgId, "", 0, c.Params(":uid"))
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
@@ -195,14 +193,14 @@ func DeleteDashboardByUid(c *middleware.Context) Response {
 	})
 	})
 }
 }
 
 
-func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
+func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	cmd.UserId = c.UserId
 	cmd.UserId = c.UserId
 
 
 	dash := cmd.GetDashboardModel()
 	dash := cmd.GetDashboardModel()
 
 
 	if dash.Id == 0 && dash.Uid == "" {
 	if dash.Id == 0 && dash.Uid == "" {
-		limitReached, err := middleware.QuotaReached(c, "dashboard")
+		limitReached, err := quota.QuotaReached(c, "dashboard")
 		if err != nil {
 		if err != nil {
 			return ApiError(500, "failed to get quota", err)
 			return ApiError(500, "failed to get quota", err)
 		}
 		}
@@ -278,7 +276,7 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
 	})
 	})
 }
 }
 
 
-func GetHomeDashboard(c *middleware.Context) Response {
+func GetHomeDashboard(c *m.ReqContext) Response {
 	prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
 	prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 		return ApiError(500, "Failed to get preferences", err)
 		return ApiError(500, "Failed to get preferences", err)
@@ -291,9 +289,8 @@ func GetHomeDashboard(c *middleware.Context) Response {
 			url := m.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
 			url := m.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
 			dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
 			dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
 			return Json(200, &dashRedirect)
 			return Json(200, &dashRedirect)
-		} else {
-			log.Warn("Failed to get slug from database, %s", err.Error())
 		}
 		}
+		log.Warn("Failed to get slug from database, %s", err.Error())
 	}
 	}
 
 
 	filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
 	filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
@@ -338,23 +335,23 @@ func addGettingStartedPanelToHomeDashboard(dash *simplejson.Json) {
 }
 }
 
 
 // GetDashboardVersions returns all dashboard versions as JSON
 // GetDashboardVersions returns all dashboard versions as JSON
-func GetDashboardVersions(c *middleware.Context) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+func GetDashboardVersions(c *m.ReqContext) Response {
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	query := m.GetDashboardVersionsQuery{
 	query := m.GetDashboardVersionsQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
-		DashboardId: dashId,
+		DashboardId: dashID,
 		Limit:       c.QueryInt("limit"),
 		Limit:       c.QueryInt("limit"),
 		Start:       c.QueryInt("start"),
 		Start:       c.QueryInt("start"),
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(404, fmt.Sprintf("No versions found for dashboardId %d", dashId), err)
+		return ApiError(404, fmt.Sprintf("No versions found for dashboardId %d", dashID), err)
 	}
 	}
 
 
 	for _, version := range query.Result {
 	for _, version := range query.Result {
@@ -377,22 +374,22 @@ func GetDashboardVersions(c *middleware.Context) Response {
 }
 }
 
 
 // GetDashboardVersion returns the dashboard version with the given ID.
 // GetDashboardVersion returns the dashboard version with the given ID.
-func GetDashboardVersion(c *middleware.Context) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+func GetDashboardVersion(c *m.ReqContext) Response {
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	query := m.GetDashboardVersionQuery{
 	query := m.GetDashboardVersionQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
-		DashboardId: dashId,
+		DashboardId: dashID,
 		Version:     c.ParamsInt(":id"),
 		Version:     c.ParamsInt(":id"),
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashId), err)
+		return ApiError(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashID), err)
 	}
 	}
 
 
 	creator := "Anonymous"
 	creator := "Anonymous"
@@ -409,7 +406,7 @@ func GetDashboardVersion(c *middleware.Context) Response {
 }
 }
 
 
 // POST /api/dashboards/calculate-diff performs diffs on two dashboards
 // POST /api/dashboards/calculate-diff performs diffs on two dashboards
-func CalculateDashboardDiff(c *middleware.Context, apiOptions dtos.CalculateDiffOptions) Response {
+func CalculateDashboardDiff(c *m.ReqContext, apiOptions dtos.CalculateDiffOptions) Response {
 
 
 	guardianBase := guardian.New(apiOptions.Base.DashboardId, c.OrgId, c.SignedInUser)
 	guardianBase := guardian.New(apiOptions.Base.DashboardId, c.OrgId, c.SignedInUser)
 	if canSave, err := guardianBase.CanSave(); err != nil || !canSave {
 	if canSave, err := guardianBase.CanSave(); err != nil || !canSave {
@@ -454,7 +451,7 @@ func CalculateDashboardDiff(c *middleware.Context, apiOptions dtos.CalculateDiff
 }
 }
 
 
 // RestoreDashboardVersion restores a dashboard to the given version.
 // RestoreDashboardVersion restores a dashboard to the given version.
-func RestoreDashboardVersion(c *middleware.Context, apiCmd dtos.RestoreDashboardVersionCommand) Response {
+func RestoreDashboardVersion(c *m.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) Response {
 	dash, rsp := getDashboardHelper(c.OrgId, "", c.ParamsInt64(":dashboardId"), "")
 	dash, rsp := getDashboardHelper(c.OrgId, "", c.ParamsInt64(":dashboardId"), "")
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
@@ -484,7 +481,7 @@ func RestoreDashboardVersion(c *middleware.Context, apiCmd dtos.RestoreDashboard
 	return PostDashboard(c, saveCmd)
 	return PostDashboard(c, saveCmd)
 }
 }
 
 
-func GetDashboardTags(c *middleware.Context) {
+func GetDashboardTags(c *m.ReqContext) {
 	query := m.GetDashboardTagsQuery{OrgId: c.OrgId}
 	query := m.GetDashboardTagsQuery{OrgId: c.OrgId}
 	err := bus.Dispatch(&query)
 	err := bus.Dispatch(&query)
 	if err != nil {
 	if err != nil {

+ 10 - 11
pkg/api/dashboard_permission.go

@@ -5,20 +5,19 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 )
 )
 
 
-func GetDashboardPermissionList(c *middleware.Context) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+func GetDashboardPermissionList(c *m.ReqContext) Response {
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
+	_, rsp := getDashboardHelper(c.OrgId, "", dashID, "")
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
 	}
 	}
 
 
-	g := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	g := guardian.New(dashID, c.OrgId, c.SignedInUser)
 
 
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
@@ -38,26 +37,26 @@ func GetDashboardPermissionList(c *middleware.Context) Response {
 	return Json(200, acl)
 	return Json(200, acl)
 }
 }
 
 
-func UpdateDashboardPermissions(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCommand) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+func UpdateDashboardPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
+	_, rsp := getDashboardHelper(c.OrgId, "", dashID, "")
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
 	}
 	}
 
 
-	g := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	g := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	cmd := m.UpdateDashboardAclCommand{}
 	cmd := m.UpdateDashboardAclCommand{}
-	cmd.DashboardId = dashId
+	cmd.DashboardId = dashID
 
 
 	for _, item := range apiCmd.Items {
 	for _, item := range apiCmd.Items {
 		cmd.Items = append(cmd.Items, &m.DashboardAcl{
 		cmd.Items = append(cmd.Items, &m.DashboardAcl{
 			OrgId:       c.OrgId,
 			OrgId:       c.OrgId,
-			DashboardId: dashId,
+			DashboardId: dashID,
 			UserId:      item.UserId,
 			UserId:      item.UserId,
 			TeamId:      item.TeamId,
 			TeamId:      item.TeamId,
 			Role:        item.Role,
 			Role:        item.Role,

+ 1 - 2
pkg/api/dashboard_permission_test.go

@@ -6,7 +6,6 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 
 
@@ -195,7 +194,7 @@ func updateDashboardPermissionScenario(desc string, url string, routePattern str
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
 
 
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID

+ 7 - 8
pkg/api/dashboard_snapshot.go

@@ -6,14 +6,13 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func GetSharingOptions(c *middleware.Context) {
+func GetSharingOptions(c *m.ReqContext) {
 	c.JSON(200, util.DynMap{
 	c.JSON(200, util.DynMap{
 		"externalSnapshotURL":  setting.ExternalSnapshotUrl,
 		"externalSnapshotURL":  setting.ExternalSnapshotUrl,
 		"externalSnapshotName": setting.ExternalSnapshotName,
 		"externalSnapshotName": setting.ExternalSnapshotName,
@@ -21,7 +20,7 @@ func GetSharingOptions(c *middleware.Context) {
 	})
 	})
 }
 }
 
 
-func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
+func CreateDashboardSnapshot(c *m.ReqContext, cmd m.CreateDashboardSnapshotCommand) {
 	if cmd.Name == "" {
 	if cmd.Name == "" {
 		cmd.Name = "Unnamed snapshot"
 		cmd.Name = "Unnamed snapshot"
 	}
 	}
@@ -58,7 +57,7 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
 }
 }
 
 
 // GET /api/snapshots/:key
 // GET /api/snapshots/:key
-func GetDashboardSnapshot(c *middleware.Context) {
+func GetDashboardSnapshot(c *m.ReqContext) {
 	key := c.Params(":key")
 	key := c.Params(":key")
 	query := &m.GetDashboardSnapshotQuery{Key: key}
 	query := &m.GetDashboardSnapshotQuery{Key: key}
 
 
@@ -93,7 +92,7 @@ func GetDashboardSnapshot(c *middleware.Context) {
 }
 }
 
 
 // GET /api/snapshots-delete/:key
 // GET /api/snapshots-delete/:key
-func DeleteDashboardSnapshot(c *middleware.Context) Response {
+func DeleteDashboardSnapshot(c *m.ReqContext) Response {
 	key := c.Params(":key")
 	key := c.Params(":key")
 
 
 	query := &m.GetDashboardSnapshotQuery{DeleteKey: key}
 	query := &m.GetDashboardSnapshotQuery{DeleteKey: key}
@@ -107,9 +106,9 @@ func DeleteDashboardSnapshot(c *middleware.Context) Response {
 		return ApiError(404, "Failed to get dashboard snapshot", nil)
 		return ApiError(404, "Failed to get dashboard snapshot", nil)
 	}
 	}
 	dashboard := query.Result.Dashboard
 	dashboard := query.Result.Dashboard
-	dashboardId := dashboard.Get("id").MustInt64()
+	dashboardID := dashboard.Get("id").MustInt64()
 
 
-	guardian := guardian.New(dashboardId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashboardID, c.OrgId, c.SignedInUser)
 	canEdit, err := guardian.CanEdit()
 	canEdit, err := guardian.CanEdit()
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Error while checking permissions for snapshot", err)
 		return ApiError(500, "Error while checking permissions for snapshot", err)
@@ -129,7 +128,7 @@ func DeleteDashboardSnapshot(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/dashboard/snapshots
 // GET /api/dashboard/snapshots
-func SearchDashboardSnapshots(c *middleware.Context) Response {
+func SearchDashboardSnapshots(c *m.ReqContext) Response {
 	query := c.Query("query")
 	query := c.Query("query")
 	limit := c.QueryInt("limit")
 	limit := c.QueryInt("limit")
 
 

+ 12 - 13
pkg/api/dashboard_test.go

@@ -8,7 +8,6 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
@@ -106,7 +105,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -166,7 +165,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -272,7 +271,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -330,7 +329,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -399,7 +398,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -469,7 +468,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -528,7 +527,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -595,7 +594,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -838,12 +837,12 @@ func CallDeleteDashboard(sc *scenarioContext) {
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 }
 }
 
 
-func CallDeleteDashboardByUid(sc *scenarioContext) {
+func CallDeleteDashboardByUID(sc *scenarioContext) {
 	bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
 	bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
 		return nil
 		return nil
 	})
 	})
 
 
-	sc.handlerFunc = DeleteDashboardByUid
+	sc.handlerFunc = DeleteDashboardByUID
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 }
 }
 
 
@@ -862,7 +861,7 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
 
 
@@ -887,7 +886,7 @@ func postDiffScenario(desc string, url string, routePattern string, cmd dtos.Cal
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.SignedInUser = &m.SignedInUser{
 			sc.context.SignedInUser = &m.SignedInUser{
 				OrgId:  TestOrgID,
 				OrgId:  TestOrgID,

+ 5 - 6
pkg/api/dataproxy.go

@@ -7,26 +7,25 @@ import (
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 )
 )
 
 
 const HeaderNameNoBackendCache = "X-Grafana-NoCache"
 const HeaderNameNoBackendCache = "X-Grafana-NoCache"
 
 
-func (hs *HttpServer) getDatasourceById(id int64, orgId int64, nocache bool) (*m.DataSource, error) {
+func (hs *HttpServer) getDatasourceByID(id int64, orgID int64, nocache bool) (*m.DataSource, error) {
 	cacheKey := fmt.Sprintf("ds-%d", id)
 	cacheKey := fmt.Sprintf("ds-%d", id)
 
 
 	if !nocache {
 	if !nocache {
 		if cached, found := hs.cache.Get(cacheKey); found {
 		if cached, found := hs.cache.Get(cacheKey); found {
 			ds := cached.(*m.DataSource)
 			ds := cached.(*m.DataSource)
-			if ds.OrgId == orgId {
+			if ds.OrgId == orgID {
 				return ds, nil
 				return ds, nil
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
+	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgID}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -35,12 +34,12 @@ func (hs *HttpServer) getDatasourceById(id int64, orgId int64, nocache bool) (*m
 	return query.Result, nil
 	return query.Result, nil
 }
 }
 
 
-func (hs *HttpServer) ProxyDataSourceRequest(c *middleware.Context) {
+func (hs *HttpServer) ProxyDataSourceRequest(c *m.ReqContext) {
 	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
 	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
 
 
 	nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
 	nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
 
 
-	ds, err := hs.getDatasourceById(c.ParamsInt64(":id"), c.OrgId, nocache)
+	ds, err := hs.getDatasourceByID(c.ParamsInt64(":id"), c.OrgId, nocache)
 
 
 	if err != nil {
 	if err != nil {
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)

+ 17 - 19
pkg/api/datasources.go

@@ -5,13 +5,12 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func GetDataSources(c *middleware.Context) Response {
+func GetDataSources(c *m.ReqContext) Response {
 	query := m.GetDataSourcesQuery{OrgId: c.OrgId}
 	query := m.GetDataSourcesQuery{OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -50,7 +49,7 @@ func GetDataSources(c *middleware.Context) Response {
 	return Json(200, &result)
 	return Json(200, &result)
 }
 }
 
 
-func GetDataSourceById(c *middleware.Context) Response {
+func GetDataSourceByID(c *m.ReqContext) Response {
 	query := m.GetDataSourceByIdQuery{
 	query := m.GetDataSourceByIdQuery{
 		Id:    c.ParamsInt64(":id"),
 		Id:    c.ParamsInt64(":id"),
 		OrgId: c.OrgId,
 		OrgId: c.OrgId,
@@ -69,14 +68,14 @@ func GetDataSourceById(c *middleware.Context) Response {
 	return Json(200, &dtos)
 	return Json(200, &dtos)
 }
 }
 
 
-func DeleteDataSourceById(c *middleware.Context) Response {
+func DeleteDataSourceByID(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	if id <= 0 {
 	if id <= 0 {
 		return ApiError(400, "Missing valid datasource id", nil)
 		return ApiError(400, "Missing valid datasource id", nil)
 	}
 	}
 
 
-	ds, err := getRawDataSourceById(id, c.OrgId)
+	ds, err := getRawDataSourceByID(id, c.OrgId)
 	if err != nil {
 	if err != nil {
 		return ApiError(400, "Failed to delete datasource", nil)
 		return ApiError(400, "Failed to delete datasource", nil)
 	}
 	}
@@ -95,7 +94,7 @@ func DeleteDataSourceById(c *middleware.Context) Response {
 	return ApiSuccess("Data source deleted")
 	return ApiSuccess("Data source deleted")
 }
 }
 
 
-func DeleteDataSourceByName(c *middleware.Context) Response {
+func DeleteDataSourceByName(c *m.ReqContext) Response {
 	name := c.Params(":name")
 	name := c.Params(":name")
 
 
 	if name == "" {
 	if name == "" {
@@ -120,7 +119,7 @@ func DeleteDataSourceByName(c *middleware.Context) Response {
 	return ApiSuccess("Data source deleted")
 	return ApiSuccess("Data source deleted")
 }
 }
 
 
-func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) Response {
+func AddDataSource(c *m.ReqContext, cmd m.AddDataSourceCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
@@ -140,11 +139,11 @@ func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) Response {
 	})
 	})
 }
 }
 
 
-func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) Response {
+func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	cmd.Id = c.ParamsInt64(":id")
 	cmd.Id = c.ParamsInt64(":id")
 
 
-	err := fillWithSecureJsonData(&cmd)
+	err := fillWithSecureJSONData(&cmd)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Failed to update datasource", err)
 		return ApiError(500, "Failed to update datasource", err)
 	}
 	}
@@ -153,9 +152,8 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) Resp
 	if err != nil {
 	if err != nil {
 		if err == m.ErrDataSourceUpdatingOldVersion {
 		if err == m.ErrDataSourceUpdatingOldVersion {
 			return ApiError(500, "Failed to update datasource. Reload new version and try again", err)
 			return ApiError(500, "Failed to update datasource. Reload new version and try again", err)
-		} else {
-			return ApiError(500, "Failed to update datasource", err)
 		}
 		}
+		return ApiError(500, "Failed to update datasource", err)
 	}
 	}
 	ds := convertModelToDtos(cmd.Result)
 	ds := convertModelToDtos(cmd.Result)
 	return Json(200, util.DynMap{
 	return Json(200, util.DynMap{
@@ -166,12 +164,12 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) Resp
 	})
 	})
 }
 }
 
 
-func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
+func fillWithSecureJSONData(cmd *m.UpdateDataSourceCommand) error {
 	if len(cmd.SecureJsonData) == 0 {
 	if len(cmd.SecureJsonData) == 0 {
 		return nil
 		return nil
 	}
 	}
 
 
-	ds, err := getRawDataSourceById(cmd.Id, cmd.OrgId)
+	ds, err := getRawDataSourceByID(cmd.Id, cmd.OrgId)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -180,8 +178,8 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
 		return m.ErrDatasourceIsReadOnly
 		return m.ErrDatasourceIsReadOnly
 	}
 	}
 
 
-	secureJsonData := ds.SecureJsonData.Decrypt()
-	for k, v := range secureJsonData {
+	secureJSONData := ds.SecureJsonData.Decrypt()
+	for k, v := range secureJSONData {
 
 
 		if _, ok := cmd.SecureJsonData[k]; !ok {
 		if _, ok := cmd.SecureJsonData[k]; !ok {
 			cmd.SecureJsonData[k] = v
 			cmd.SecureJsonData[k] = v
@@ -191,10 +189,10 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
 	return nil
 	return nil
 }
 }
 
 
-func getRawDataSourceById(id int64, orgId int64) (*m.DataSource, error) {
+func getRawDataSourceByID(id int64, orgID int64) (*m.DataSource, error) {
 	query := m.GetDataSourceByIdQuery{
 	query := m.GetDataSourceByIdQuery{
 		Id:    id,
 		Id:    id,
-		OrgId: orgId,
+		OrgId: orgID,
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -205,7 +203,7 @@ func getRawDataSourceById(id int64, orgId int64) (*m.DataSource, error) {
 }
 }
 
 
 // Get /api/datasources/name/:name
 // Get /api/datasources/name/:name
-func GetDataSourceByName(c *middleware.Context) Response {
+func GetDataSourceByName(c *m.ReqContext) Response {
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -221,7 +219,7 @@ func GetDataSourceByName(c *middleware.Context) Response {
 }
 }
 
 
 // Get /api/datasources/id/:name
 // Get /api/datasources/id/:name
-func GetDataSourceIdByName(c *middleware.Context) Response {
+func GetDataSourceIDByName(c *m.ReqContext) Response {
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 6 - 7
pkg/api/folder.go

@@ -4,14 +4,13 @@ import (
 	"fmt"
 	"fmt"
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func GetFolders(c *middleware.Context) Response {
+func GetFolders(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folders, err := s.GetFolders(c.QueryInt("limit"))
 	folders, err := s.GetFolders(c.QueryInt("limit"))
 
 
@@ -32,7 +31,7 @@ func GetFolders(c *middleware.Context) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetFolderByUid(c *middleware.Context) Response {
+func GetFolderByUID(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 
 
@@ -44,7 +43,7 @@ func GetFolderByUid(c *middleware.Context) Response {
 	return Json(200, toFolderDto(g, folder))
 	return Json(200, toFolderDto(g, folder))
 }
 }
 
 
-func GetFolderById(c *middleware.Context) Response {
+func GetFolderByID(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderById(c.ParamsInt64(":id"))
 	folder, err := s.GetFolderById(c.ParamsInt64(":id"))
 	if err != nil {
 	if err != nil {
@@ -55,7 +54,7 @@ func GetFolderById(c *middleware.Context) Response {
 	return Json(200, toFolderDto(g, folder))
 	return Json(200, toFolderDto(g, folder))
 }
 }
 
 
-func CreateFolder(c *middleware.Context, cmd m.CreateFolderCommand) Response {
+func CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	err := s.CreateFolder(&cmd)
 	err := s.CreateFolder(&cmd)
 	if err != nil {
 	if err != nil {
@@ -66,7 +65,7 @@ func CreateFolder(c *middleware.Context, cmd m.CreateFolderCommand) Response {
 	return Json(200, toFolderDto(g, cmd.Result))
 	return Json(200, toFolderDto(g, cmd.Result))
 }
 }
 
 
-func UpdateFolder(c *middleware.Context, cmd m.UpdateFolderCommand) Response {
+func UpdateFolder(c *m.ReqContext, cmd m.UpdateFolderCommand) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	err := s.UpdateFolder(c.Params(":uid"), &cmd)
 	err := s.UpdateFolder(c.Params(":uid"), &cmd)
 	if err != nil {
 	if err != nil {
@@ -77,7 +76,7 @@ func UpdateFolder(c *middleware.Context, cmd m.UpdateFolderCommand) Response {
 	return Json(200, toFolderDto(g, cmd.Result))
 	return Json(200, toFolderDto(g, cmd.Result))
 }
 }
 
 
-func DeleteFolder(c *middleware.Context) Response {
+func DeleteFolder(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	f, err := s.DeleteFolder(c.Params(":uid"))
 	f, err := s.DeleteFolder(c.Params(":uid"))
 	if err != nil {
 	if err != nil {

+ 2 - 3
pkg/api/folder_permission.go

@@ -5,13 +5,12 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 )
 )
 
 
-func GetFolderPermissionList(c *middleware.Context) Response {
+func GetFolderPermissionList(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 
 
@@ -42,7 +41,7 @@ func GetFolderPermissionList(c *middleware.Context) Response {
 	return Json(200, acl)
 	return Json(200, acl)
 }
 }
 
 
-func UpdateFolderPermissions(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCommand) Response {
+func UpdateFolderPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 
 

+ 1 - 2
pkg/api/folder_permission_test.go

@@ -6,7 +6,6 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
@@ -227,7 +226,7 @@ func updateFolderPermissionScenario(desc string, url string, routePattern string
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
 
 
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.OrgId = TestOrgID
 			sc.context.OrgId = TestOrgID
 			sc.context.UserId = TestUserID
 			sc.context.UserId = TestUserID

+ 18 - 21
pkg/api/folder_test.go

@@ -7,11 +7,8 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/services/dashboards"
-
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/dashboards"
 
 
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
 )
 )
@@ -136,8 +133,8 @@ func TestFoldersApiEndpoint(t *testing.T) {
 	})
 	})
 }
 }
 
 
-func callGetFolderByUid(sc *scenarioContext) {
-	sc.handlerFunc = GetFolderByUid
+func callGetFolderByUID(sc *scenarioContext) {
+	sc.handlerFunc = GetFolderByUID
 	sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
 }
 }
 
 
@@ -155,7 +152,7 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
 
 
@@ -184,7 +181,7 @@ func updateFolderScenario(desc string, url string, routePattern string, mock *fa
 		defer bus.ClearBusHandlers()
 		defer bus.ClearBusHandlers()
 
 
 		sc := setupScenarioContext(url)
 		sc := setupScenarioContext(url)
-		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+		sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
 			sc.context = c
 			sc.context = c
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
 			sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
 
 
@@ -205,50 +202,50 @@ func updateFolderScenario(desc string, url string, routePattern string, mock *fa
 }
 }
 
 
 type fakeFolderService struct {
 type fakeFolderService struct {
-	GetFoldersResult     []*models.Folder
+	GetFoldersResult     []*m.Folder
 	GetFoldersError      error
 	GetFoldersError      error
-	GetFolderByUidResult *models.Folder
+	GetFolderByUidResult *m.Folder
 	GetFolderByUidError  error
 	GetFolderByUidError  error
-	GetFolderByIdResult  *models.Folder
+	GetFolderByIdResult  *m.Folder
 	GetFolderByIdError   error
 	GetFolderByIdError   error
-	CreateFolderResult   *models.Folder
+	CreateFolderResult   *m.Folder
 	CreateFolderError    error
 	CreateFolderError    error
-	UpdateFolderResult   *models.Folder
+	UpdateFolderResult   *m.Folder
 	UpdateFolderError    error
 	UpdateFolderError    error
-	DeleteFolderResult   *models.Folder
+	DeleteFolderResult   *m.Folder
 	DeleteFolderError    error
 	DeleteFolderError    error
 	DeletedFolderUids    []string
 	DeletedFolderUids    []string
 }
 }
 
 
-func (s *fakeFolderService) GetFolders(limit int) ([]*models.Folder, error) {
+func (s *fakeFolderService) GetFolders(limit int) ([]*m.Folder, error) {
 	return s.GetFoldersResult, s.GetFoldersError
 	return s.GetFoldersResult, s.GetFoldersError
 }
 }
 
 
-func (s *fakeFolderService) GetFolderById(id int64) (*models.Folder, error) {
+func (s *fakeFolderService) GetFolderById(id int64) (*m.Folder, error) {
 	return s.GetFolderByIdResult, s.GetFolderByIdError
 	return s.GetFolderByIdResult, s.GetFolderByIdError
 }
 }
 
 
-func (s *fakeFolderService) GetFolderByUid(uid string) (*models.Folder, error) {
+func (s *fakeFolderService) GetFolderByUid(uid string) (*m.Folder, error) {
 	return s.GetFolderByUidResult, s.GetFolderByUidError
 	return s.GetFolderByUidResult, s.GetFolderByUidError
 }
 }
 
 
-func (s *fakeFolderService) CreateFolder(cmd *models.CreateFolderCommand) error {
+func (s *fakeFolderService) CreateFolder(cmd *m.CreateFolderCommand) error {
 	cmd.Result = s.CreateFolderResult
 	cmd.Result = s.CreateFolderResult
 	return s.CreateFolderError
 	return s.CreateFolderError
 }
 }
 
 
-func (s *fakeFolderService) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
+func (s *fakeFolderService) UpdateFolder(existingUid string, cmd *m.UpdateFolderCommand) error {
 	cmd.Result = s.UpdateFolderResult
 	cmd.Result = s.UpdateFolderResult
 	return s.UpdateFolderError
 	return s.UpdateFolderError
 }
 }
 
 
-func (s *fakeFolderService) DeleteFolder(uid string) (*models.Folder, error) {
+func (s *fakeFolderService) DeleteFolder(uid string) (*m.Folder, error) {
 	s.DeletedFolderUids = append(s.DeletedFolderUids, uid)
 	s.DeletedFolderUids = append(s.DeletedFolderUids, uid)
 	return s.DeleteFolderResult, s.DeleteFolderError
 	return s.DeleteFolderResult, s.DeleteFolderError
 }
 }
 
 
 func mockFolderService(mock *fakeFolderService) {
 func mockFolderService(mock *fakeFolderService) {
-	dashboards.NewFolderService = func(orgId int64, user *models.SignedInUser) dashboards.FolderService {
+	dashboards.NewFolderService = func(orgId int64, user *m.SignedInUser) dashboards.FolderService {
 		return mock
 		return mock
 	}
 	}
 }
 }

+ 2 - 3
pkg/api/frontendsettings.go

@@ -5,14 +5,13 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) {
+func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) {
 	orgDataSources := make([]*m.DataSource, 0)
 	orgDataSources := make([]*m.DataSource, 0)
 
 
 	if c.OrgId != 0 {
 	if c.OrgId != 0 {
@@ -180,7 +179,7 @@ func getPanelSort(id string) int {
 	return sort
 	return sort
 }
 }
 
 
-func GetFrontendSettings(c *middleware.Context) {
+func GetFrontendSettings(c *m.ReqContext) {
 	settings, err := getFrontendSettingsMap(c)
 	settings, err := getFrontendSettingsMap(c)
 	if err != nil {
 	if err != nil {
 		c.JsonApiErr(400, "Failed to get frontend settings", err)
 		c.JsonApiErr(400, "Failed to get frontend settings", err)

+ 2 - 2
pkg/api/grafana_com_proxy.go

@@ -7,7 +7,7 @@ import (
 	"net/url"
 	"net/url"
 	"time"
 	"time"
 
 
-	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
@@ -41,7 +41,7 @@ func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy {
 	return &httputil.ReverseProxy{Director: director}
 	return &httputil.ReverseProxy{Director: director}
 }
 }
 
 
-func ProxyGnetRequest(c *middleware.Context) {
+func ProxyGnetRequest(c *m.ReqContext) {
 	proxyPath := c.Params("*")
 	proxyPath := c.Params("*")
 	proxy := ReverseProxyGnetReq(proxyPath)
 	proxy := ReverseProxyGnetReq(proxyPath)
 	proxy.Transport = grafanaComProxyTransport
 	proxy.Transport = grafanaComProxyTransport

+ 2 - 2
pkg/api/http_server.go

@@ -39,7 +39,7 @@ type HttpServer struct {
 	httpSrv *http.Server
 	httpSrv *http.Server
 }
 }
 
 
-func NewHttpServer() *HttpServer {
+func NewHTTPServer() *HttpServer {
 	return &HttpServer{
 	return &HttpServer{
 		log:   log.New("http.server"),
 		log:   log.New("http.server"),
 		cache: gocache.New(5*time.Minute, 10*time.Minute),
 		cache: gocache.New(5*time.Minute, 10*time.Minute),
@@ -175,7 +175,7 @@ func (hs *HttpServer) newMacaron() *macaron.Macaron {
 	m.Use(hs.healthHandler)
 	m.Use(hs.healthHandler)
 	m.Use(hs.metricsEndpoint)
 	m.Use(hs.metricsEndpoint)
 	m.Use(middleware.GetContextHandler())
 	m.Use(middleware.GetContextHandler())
-	m.Use(middleware.Sessioner(&setting.SessionOptions))
+	m.Use(middleware.Sessioner(&setting.SessionOptions, setting.SessionConnMaxLifetime))
 	m.Use(middleware.OrgRedirect())
 	m.Use(middleware.OrgRedirect())
 
 
 	// needs to be after context handler
 	// needs to be after context handler

+ 19 - 19
pkg/api/index.go

@@ -6,13 +6,12 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
-func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
+func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 	settings, err := getFrontendSettingsMap(c)
 	settings, err := getFrontendSettingsMap(c)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -33,13 +32,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 		locale = parts[0]
 		locale = parts[0]
 	}
 	}
 
 
-	appUrl := setting.AppUrl
-	appSubUrl := setting.AppSubUrl
+	appURL := setting.AppUrl
+	appSubURL := setting.AppSubUrl
 
 
 	// special case when doing localhost call from phantomjs
 	// special case when doing localhost call from phantomjs
 	if c.IsRenderCall {
 	if c.IsRenderCall {
-		appUrl = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
-		appSubUrl = ""
+		appURL = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
+		appSubURL = ""
 		settings["appSubUrl"] = ""
 		settings["appSubUrl"] = ""
 	}
 	}
 
 
@@ -63,8 +62,8 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 		},
 		},
 		Settings:                settings,
 		Settings:                settings,
 		Theme:                   prefs.Theme,
 		Theme:                   prefs.Theme,
-		AppUrl:                  appUrl,
-		AppSubUrl:               appSubUrl,
+		AppUrl:                  appURL,
+		AppSubUrl:               appSubURL,
 		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
 		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
 		GoogleTagManagerId:      setting.GoogleTagManagerId,
 		GoogleTagManagerId:      setting.GoogleTagManagerId,
 		BuildVersion:            setting.BuildVersion,
 		BuildVersion:            setting.BuildVersion,
@@ -74,15 +73,15 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 	}
 	}
 
 
 	if setting.DisableGravatar {
 	if setting.DisableGravatar {
-		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/transparent.png"
+		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/user_profile.png"
 	}
 	}
 
 
 	if len(data.User.Name) == 0 {
 	if len(data.User.Name) == 0 {
 		data.User.Name = data.User.Login
 		data.User.Name = data.User.Login
 	}
 	}
 
 
-	themeUrlParam := c.Query("theme")
-	if themeUrlParam == "light" {
+	themeURLParam := c.Query("theme")
+	if themeURLParam == "light" {
 		data.User.LightTheme = true
 		data.User.LightTheme = true
 		data.Theme = "light"
 		data.Theme = "light"
 	}
 	}
@@ -299,25 +298,26 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 	return &data, nil
 	return &data, nil
 }
 }
 
 
-func Index(c *middleware.Context) {
-	if data, err := setIndexViewData(c); err != nil {
+func Index(c *m.ReqContext) {
+	data, err := setIndexViewData(c)
+	if err != nil {
 		c.Handle(500, "Failed to get settings", err)
 		c.Handle(500, "Failed to get settings", err)
 		return
 		return
-	} else {
-		c.HTML(200, "index", data)
 	}
 	}
+	c.HTML(200, "index", data)
 }
 }
 
 
-func NotFoundHandler(c *middleware.Context) {
+func NotFoundHandler(c *m.ReqContext) {
 	if c.IsApiRequest() {
 	if c.IsApiRequest() {
 		c.JsonApiErr(404, "Not found", nil)
 		c.JsonApiErr(404, "Not found", nil)
 		return
 		return
 	}
 	}
 
 
-	if data, err := setIndexViewData(c); err != nil {
+	data, err := setIndexViewData(c)
+	if err != nil {
 		c.Handle(500, "Failed to get settings", err)
 		c.Handle(500, "Failed to get settings", err)
 		return
 		return
-	} else {
-		c.HTML(404, "index", data)
 	}
 	}
+
+	c.HTML(404, "index", data)
 }
 }

+ 12 - 12
pkg/api/login.go

@@ -8,16 +8,16 @@ import (
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
 const (
 const (
-	VIEW_INDEX = "index"
+	ViewIndex = "index"
 )
 )
 
 
-func LoginView(c *middleware.Context) {
+func LoginView(c *m.ReqContext) {
 	viewData, err := setIndexViewData(c)
 	viewData, err := setIndexViewData(c)
 	if err != nil {
 	if err != nil {
 		c.Handle(500, "Failed to get settings", err)
 		c.Handle(500, "Failed to get settings", err)
@@ -40,7 +40,7 @@ func LoginView(c *middleware.Context) {
 	}
 	}
 
 
 	if !tryLoginUsingRememberCookie(c) {
 	if !tryLoginUsingRememberCookie(c) {
-		c.HTML(200, VIEW_INDEX, viewData)
+		c.HTML(200, ViewIndex, viewData)
 		return
 		return
 	}
 	}
 
 
@@ -53,7 +53,7 @@ func LoginView(c *middleware.Context) {
 	c.Redirect(setting.AppSubUrl + "/")
 	c.Redirect(setting.AppSubUrl + "/")
 }
 }
 
 
-func tryLoginUsingRememberCookie(c *middleware.Context) bool {
+func tryLoginUsingRememberCookie(c *m.ReqContext) bool {
 	// Check auto-login.
 	// Check auto-login.
 	uname := c.GetCookie(setting.CookieUserName)
 	uname := c.GetCookie(setting.CookieUserName)
 	if len(uname) == 0 {
 	if len(uname) == 0 {
@@ -87,7 +87,7 @@ func tryLoginUsingRememberCookie(c *middleware.Context) bool {
 	return true
 	return true
 }
 }
 
 
-func LoginApiPing(c *middleware.Context) {
+func LoginAPIPing(c *m.ReqContext) {
 	if !tryLoginUsingRememberCookie(c) {
 	if !tryLoginUsingRememberCookie(c) {
 		c.JsonApiErr(401, "Unauthorized", nil)
 		c.JsonApiErr(401, "Unauthorized", nil)
 		return
 		return
@@ -96,7 +96,7 @@ func LoginApiPing(c *middleware.Context) {
 	c.JsonOK("Logged in")
 	c.JsonOK("Logged in")
 }
 }
 
 
-func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) Response {
+func LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response {
 	if setting.DisableLoginForm {
 	if setting.DisableLoginForm {
 		return ApiError(401, "Login is disabled", nil)
 		return ApiError(401, "Login is disabled", nil)
 	}
 	}
@@ -133,7 +133,7 @@ func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func loginUserWithUser(user *m.User, c *middleware.Context) {
+func loginUserWithUser(user *m.User, c *m.ReqContext) {
 	if user == nil {
 	if user == nil {
 		log.Error(3, "User login with nil user")
 		log.Error(3, "User login with nil user")
 	}
 	}
@@ -146,13 +146,13 @@ func loginUserWithUser(user *m.User, c *middleware.Context) {
 		c.SetSuperSecureCookie(user.Rands+user.Password, setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/")
 		c.SetSuperSecureCookie(user.Rands+user.Password, setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/")
 	}
 	}
 
 
-	c.Session.RegenerateId(c)
-	c.Session.Set(middleware.SESS_KEY_USERID, user.Id)
+	c.Session.RegenerateId(c.Context)
+	c.Session.Set(session.SESS_KEY_USERID, user.Id)
 }
 }
 
 
-func Logout(c *middleware.Context) {
+func Logout(c *m.ReqContext) {
 	c.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl+"/")
 	c.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl+"/")
 	c.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl+"/")
 	c.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl+"/")
-	c.Session.Destory(c)
+	c.Session.Destory(c.Context)
 	c.Redirect(setting.AppSubUrl + "/login")
 	c.Redirect(setting.AppSubUrl + "/login")
 }
 }

+ 7 - 6
pkg/api/login_oauth.go

@@ -17,8 +17,9 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/quota"
+	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/social"
 	"github.com/grafana/grafana/pkg/social"
 )
 )
@@ -38,7 +39,7 @@ func GenStateString() string {
 	return base64.URLEncoding.EncodeToString(rnd)
 	return base64.URLEncoding.EncodeToString(rnd)
 }
 }
 
 
-func OAuthLogin(ctx *middleware.Context) {
+func OAuthLogin(ctx *m.ReqContext) {
 	if setting.OAuthService == nil {
 	if setting.OAuthService == nil {
 		ctx.Handle(404, "OAuth not enabled", nil)
 		ctx.Handle(404, "OAuth not enabled", nil)
 		return
 		return
@@ -62,7 +63,7 @@ func OAuthLogin(ctx *middleware.Context) {
 	code := ctx.Query("code")
 	code := ctx.Query("code")
 	if code == "" {
 	if code == "" {
 		state := GenStateString()
 		state := GenStateString()
-		ctx.Session.Set(middleware.SESS_KEY_OAUTH_STATE, state)
+		ctx.Session.Set(session.SESS_KEY_OAUTH_STATE, state)
 		if setting.OAuthService.OAuthInfos[name].HostedDomain == "" {
 		if setting.OAuthService.OAuthInfos[name].HostedDomain == "" {
 			ctx.Redirect(connect.AuthCodeURL(state, oauth2.AccessTypeOnline))
 			ctx.Redirect(connect.AuthCodeURL(state, oauth2.AccessTypeOnline))
 		} else {
 		} else {
@@ -71,7 +72,7 @@ func OAuthLogin(ctx *middleware.Context) {
 		return
 		return
 	}
 	}
 
 
-	savedState, ok := ctx.Session.Get(middleware.SESS_KEY_OAUTH_STATE).(string)
+	savedState, ok := ctx.Session.Get(session.SESS_KEY_OAUTH_STATE).(string)
 	if !ok {
 	if !ok {
 		ctx.Handle(500, "login.OAuthLogin(missing saved state)", nil)
 		ctx.Handle(500, "login.OAuthLogin(missing saved state)", nil)
 		return
 		return
@@ -167,7 +168,7 @@ func OAuthLogin(ctx *middleware.Context) {
 			redirectWithError(ctx, ErrSignUpNotAllowed)
 			redirectWithError(ctx, ErrSignUpNotAllowed)
 			return
 			return
 		}
 		}
-		limitReached, err := middleware.QuotaReached(ctx, "user")
+		limitReached, err := quota.QuotaReached(ctx, "user")
 		if err != nil {
 		if err != nil {
 			ctx.Handle(500, "Failed to get user quota", err)
 			ctx.Handle(500, "Failed to get user quota", err)
 			return
 			return
@@ -208,7 +209,7 @@ func OAuthLogin(ctx *middleware.Context) {
 	ctx.Redirect(setting.AppSubUrl + "/")
 	ctx.Redirect(setting.AppSubUrl + "/")
 }
 }
 
 
-func redirectWithError(ctx *middleware.Context, err error, v ...interface{}) {
+func redirectWithError(ctx *m.ReqContext, err error, v ...interface{}) {
 	ctx.Logger.Error(err.Error(), v...)
 	ctx.Logger.Error(err.Error(), v...)
 	ctx.Session.Set("loginError", err.Error())
 	ctx.Session.Set("loginError", err.Error())
 	ctx.Redirect(setting.AppSubUrl + "/login")
 	ctx.Redirect(setting.AppSubUrl + "/login")

+ 10 - 11
pkg/api/metrics.go

@@ -6,27 +6,26 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/models"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb/testdata"
 	"github.com/grafana/grafana/pkg/tsdb/testdata"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // POST /api/tsdb/query
 // POST /api/tsdb/query
-func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
+func QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) Response {
 	timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To)
 	timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To)
 
 
 	if len(reqDto.Queries) == 0 {
 	if len(reqDto.Queries) == 0 {
 		return ApiError(400, "No queries found in query", nil)
 		return ApiError(400, "No queries found in query", nil)
 	}
 	}
 
 
-	dsId, err := reqDto.Queries[0].Get("datasourceId").Int64()
+	dsID, err := reqDto.Queries[0].Get("datasourceId").Int64()
 	if err != nil {
 	if err != nil {
 		return ApiError(400, "Query missing datasourceId", nil)
 		return ApiError(400, "Query missing datasourceId", nil)
 	}
 	}
 
 
-	dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
+	dsQuery := m.GetDataSourceByIdQuery{Id: dsID, OrgId: c.OrgId}
 	if err := bus.Dispatch(&dsQuery); err != nil {
 	if err := bus.Dispatch(&dsQuery); err != nil {
 		return ApiError(500, "failed to fetch data source", err)
 		return ApiError(500, "failed to fetch data source", err)
 	}
 	}
@@ -61,7 +60,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
 }
 }
 
 
 // GET /api/tsdb/testdata/scenarios
 // GET /api/tsdb/testdata/scenarios
-func GetTestDataScenarios(c *middleware.Context) Response {
+func GetTestDataScenarios(c *m.ReqContext) Response {
 	result := make([]interface{}, 0)
 	result := make([]interface{}, 0)
 
 
 	for _, scenario := range testdata.ScenarioRegistry {
 	for _, scenario := range testdata.ScenarioRegistry {
@@ -77,14 +76,14 @@ func GetTestDataScenarios(c *middleware.Context) Response {
 }
 }
 
 
 // Genereates a index out of range error
 // Genereates a index out of range error
-func GenerateError(c *middleware.Context) Response {
+func GenerateError(c *m.ReqContext) Response {
 	var array []string
 	var array []string
 	return Json(200, array[20])
 	return Json(200, array[20])
 }
 }
 
 
 // GET /api/tsdb/testdata/gensql
 // GET /api/tsdb/testdata/gensql
-func GenerateSqlTestData(c *middleware.Context) Response {
-	if err := bus.Dispatch(&models.InsertSqlTestDataCommand{}); err != nil {
+func GenerateSQLTestData(c *m.ReqContext) Response {
+	if err := bus.Dispatch(&m.InsertSqlTestDataCommand{}); err != nil {
 		return ApiError(500, "Failed to insert test data", err)
 		return ApiError(500, "Failed to insert test data", err)
 	}
 	}
 
 
@@ -92,7 +91,7 @@ func GenerateSqlTestData(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/tsdb/testdata/random-walk
 // GET /api/tsdb/testdata/random-walk
-func GetTestDataRandomWalk(c *middleware.Context) Response {
+func GetTestDataRandomWalk(c *m.ReqContext) Response {
 	from := c.Query("from")
 	from := c.Query("from")
 	to := c.Query("to")
 	to := c.Query("to")
 	intervalMs := c.QueryInt64("intervalMs")
 	intervalMs := c.QueryInt64("intervalMs")
@@ -100,7 +99,7 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
 	timeRange := tsdb.NewTimeRange(from, to)
 	timeRange := tsdb.NewTimeRange(from, to)
 	request := &tsdb.TsdbQuery{TimeRange: timeRange}
 	request := &tsdb.TsdbQuery{TimeRange: timeRange}
 
 
-	dsInfo := &models.DataSource{Type: "grafana-testdata-datasource"}
+	dsInfo := &m.DataSource{Type: "grafana-testdata-datasource"}
 	request.Queries = append(request.Queries, &tsdb.Query{
 	request.Queries = append(request.Queries, &tsdb.Query{
 		RefId:      "A",
 		RefId:      "A",
 		IntervalMs: intervalMs,
 		IntervalMs: intervalMs,

+ 14 - 15
pkg/api/org.go

@@ -4,24 +4,23 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // GET /api/org
 // GET /api/org
-func GetOrgCurrent(c *middleware.Context) Response {
+func GetOrgCurrent(c *m.ReqContext) Response {
 	return getOrgHelper(c.OrgId)
 	return getOrgHelper(c.OrgId)
 }
 }
 
 
 // GET /api/orgs/:orgId
 // GET /api/orgs/:orgId
-func GetOrgById(c *middleware.Context) Response {
+func GetOrgByID(c *m.ReqContext) Response {
 	return getOrgHelper(c.ParamsInt64(":orgId"))
 	return getOrgHelper(c.ParamsInt64(":orgId"))
 }
 }
 
 
 // Get /api/orgs/name/:name
 // Get /api/orgs/name/:name
-func GetOrgByName(c *middleware.Context) Response {
+func GetOrgByName(c *m.ReqContext) Response {
 	query := m.GetOrgByNameQuery{Name: c.Params(":name")}
 	query := m.GetOrgByNameQuery{Name: c.Params(":name")}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		if err == m.ErrOrgNotFound {
 		if err == m.ErrOrgNotFound {
@@ -76,7 +75,7 @@ func getOrgHelper(orgId int64) Response {
 }
 }
 
 
 // POST /api/orgs
 // POST /api/orgs
-func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) Response {
+func CreateOrg(c *m.ReqContext, cmd m.CreateOrgCommand) Response {
 	if !c.IsSignedIn || (!setting.AllowUserOrgCreate && !c.IsGrafanaAdmin) {
 	if !c.IsSignedIn || (!setting.AllowUserOrgCreate && !c.IsGrafanaAdmin) {
 		return ApiError(403, "Access denied", nil)
 		return ApiError(403, "Access denied", nil)
 	}
 	}
@@ -98,17 +97,17 @@ func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) Response {
 }
 }
 
 
 // PUT /api/org
 // PUT /api/org
-func UpdateOrgCurrent(c *middleware.Context, form dtos.UpdateOrgForm) Response {
+func UpdateOrgCurrent(c *m.ReqContext, form dtos.UpdateOrgForm) Response {
 	return updateOrgHelper(form, c.OrgId)
 	return updateOrgHelper(form, c.OrgId)
 }
 }
 
 
 // PUT /api/orgs/:orgId
 // PUT /api/orgs/:orgId
-func UpdateOrg(c *middleware.Context, form dtos.UpdateOrgForm) Response {
+func UpdateOrg(c *m.ReqContext, form dtos.UpdateOrgForm) Response {
 	return updateOrgHelper(form, c.ParamsInt64(":orgId"))
 	return updateOrgHelper(form, c.ParamsInt64(":orgId"))
 }
 }
 
 
-func updateOrgHelper(form dtos.UpdateOrgForm, orgId int64) Response {
-	cmd := m.UpdateOrgCommand{Name: form.Name, OrgId: orgId}
+func updateOrgHelper(form dtos.UpdateOrgForm, orgID int64) Response {
+	cmd := m.UpdateOrgCommand{Name: form.Name, OrgId: orgID}
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		if err == m.ErrOrgNameTaken {
 		if err == m.ErrOrgNameTaken {
 			return ApiError(400, "Organization name taken", err)
 			return ApiError(400, "Organization name taken", err)
@@ -120,18 +119,18 @@ func updateOrgHelper(form dtos.UpdateOrgForm, orgId int64) Response {
 }
 }
 
 
 // PUT /api/org/address
 // PUT /api/org/address
-func UpdateOrgAddressCurrent(c *middleware.Context, form dtos.UpdateOrgAddressForm) Response {
+func UpdateOrgAddressCurrent(c *m.ReqContext, form dtos.UpdateOrgAddressForm) Response {
 	return updateOrgAddressHelper(form, c.OrgId)
 	return updateOrgAddressHelper(form, c.OrgId)
 }
 }
 
 
 // PUT /api/orgs/:orgId/address
 // PUT /api/orgs/:orgId/address
-func UpdateOrgAddress(c *middleware.Context, form dtos.UpdateOrgAddressForm) Response {
+func UpdateOrgAddress(c *m.ReqContext, form dtos.UpdateOrgAddressForm) Response {
 	return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
 	return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
 }
 }
 
 
-func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgId int64) Response {
+func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgID int64) Response {
 	cmd := m.UpdateOrgAddressCommand{
 	cmd := m.UpdateOrgAddressCommand{
-		OrgId: orgId,
+		OrgId: orgID,
 		Address: m.Address{
 		Address: m.Address{
 			Address1: form.Address1,
 			Address1: form.Address1,
 			Address2: form.Address2,
 			Address2: form.Address2,
@@ -150,7 +149,7 @@ func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgId int64) Respons
 }
 }
 
 
 // GET /api/orgs/:orgId
 // GET /api/orgs/:orgId
-func DeleteOrgById(c *middleware.Context) Response {
+func DeleteOrgByID(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
 	if err := bus.Dispatch(&m.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
 		if err == m.ErrOrgNotFound {
 		if err == m.ErrOrgNotFound {
 			return ApiError(404, "Failed to delete organization. ID not found", nil)
 			return ApiError(404, "Failed to delete organization. ID not found", nil)
@@ -160,7 +159,7 @@ func DeleteOrgById(c *middleware.Context) Response {
 	return ApiSuccess("Organization deleted")
 	return ApiSuccess("Organization deleted")
 }
 }
 
 
-func SearchOrgs(c *middleware.Context) Response {
+func SearchOrgs(c *m.ReqContext) Response {
 	query := m.SearchOrgsQuery{
 	query := m.SearchOrgsQuery{
 		Query: c.Query("query"),
 		Query: c.Query("query"),
 		Name:  c.Query("name"),
 		Name:  c.Query("name"),

+ 21 - 23
pkg/api/org_invite.go

@@ -7,13 +7,12 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func GetPendingOrgInvites(c *middleware.Context) Response {
+func GetPendingOrgInvites(c *m.ReqContext) Response {
 	query := m.GetTempUsersQuery{OrgId: c.OrgId, Status: m.TmpUserInvitePending}
 	query := m.GetTempUsersQuery{OrgId: c.OrgId, Status: m.TmpUserInvitePending}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -27,7 +26,7 @@ func GetPendingOrgInvites(c *middleware.Context) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response {
+func AddOrgInvite(c *m.ReqContext, inviteDto dtos.AddInviteForm) Response {
 	if !inviteDto.Role.IsValid() {
 	if !inviteDto.Role.IsValid() {
 		return ApiError(400, "Invalid role specified", nil)
 		return ApiError(400, "Invalid role specified", nil)
 	}
 	}
@@ -89,7 +88,7 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response
 	return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail))
 	return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail))
 }
 }
 
 
-func inviteExistingUserToOrg(c *middleware.Context, user *m.User, inviteDto *dtos.AddInviteForm) Response {
+func inviteExistingUserToOrg(c *m.ReqContext, user *m.User, inviteDto *dtos.AddInviteForm) Response {
 	// user exists, add org role
 	// user exists, add org role
 	createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: user.Id, Role: inviteDto.Role}
 	createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: user.Id, Role: inviteDto.Role}
 	if err := bus.Dispatch(&createOrgUserCmd); err != nil {
 	if err := bus.Dispatch(&createOrgUserCmd); err != nil {
@@ -97,29 +96,28 @@ func inviteExistingUserToOrg(c *middleware.Context, user *m.User, inviteDto *dto
 			return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
 			return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
 		}
 		}
 		return ApiError(500, "Error while trying to create org user", err)
 		return ApiError(500, "Error while trying to create org user", err)
-	} else {
+	}
 
 
-		if inviteDto.SendEmail && util.IsEmail(user.Email) {
-			emailCmd := m.SendEmailCommand{
-				To:       []string{user.Email},
-				Template: "invited_to_org.html",
-				Data: map[string]interface{}{
-					"Name":      user.NameOrFallback(),
-					"OrgName":   c.OrgName,
-					"InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login),
-				},
-			}
-
-			if err := bus.Dispatch(&emailCmd); err != nil {
-				return ApiError(500, "Failed to send email invited_to_org", err)
-			}
+	if inviteDto.SendEmail && util.IsEmail(user.Email) {
+		emailCmd := m.SendEmailCommand{
+			To:       []string{user.Email},
+			Template: "invited_to_org.html",
+			Data: map[string]interface{}{
+				"Name":      user.NameOrFallback(),
+				"OrgName":   c.OrgName,
+				"InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login),
+			},
 		}
 		}
 
 
-		return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName))
+		if err := bus.Dispatch(&emailCmd); err != nil {
+			return ApiError(500, "Failed to send email invited_to_org", err)
+		}
 	}
 	}
+
+	return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName))
 }
 }
 
 
-func RevokeInvite(c *middleware.Context) Response {
+func RevokeInvite(c *m.ReqContext) Response {
 	if ok, rsp := updateTempUserStatus(c.Params(":code"), m.TmpUserRevoked); !ok {
 	if ok, rsp := updateTempUserStatus(c.Params(":code"), m.TmpUserRevoked); !ok {
 		return rsp
 		return rsp
 	}
 	}
@@ -127,7 +125,7 @@ func RevokeInvite(c *middleware.Context) Response {
 	return ApiSuccess("Invite revoked")
 	return ApiSuccess("Invite revoked")
 }
 }
 
 
-func GetInviteInfoByCode(c *middleware.Context) Response {
+func GetInviteInfoByCode(c *m.ReqContext) Response {
 	query := m.GetTempUserByCodeQuery{Code: c.Params(":code")}
 	query := m.GetTempUserByCodeQuery{Code: c.Params(":code")}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -147,7 +145,7 @@ func GetInviteInfoByCode(c *middleware.Context) Response {
 	})
 	})
 }
 }
 
 
-func CompleteInvite(c *middleware.Context, completeInvite dtos.CompleteInviteForm) Response {
+func CompleteInvite(c *m.ReqContext, completeInvite dtos.CompleteInviteForm) Response {
 	query := m.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
 	query := m.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 17 - 18
pkg/api/org_users.go

@@ -3,18 +3,17 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
 // POST /api/org/users
 // POST /api/org/users
-func AddOrgUserToCurrentOrg(c *middleware.Context, cmd m.AddOrgUserCommand) Response {
+func AddOrgUserToCurrentOrg(c *m.ReqContext, cmd m.AddOrgUserCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	return addOrgUserHelper(cmd)
 	return addOrgUserHelper(cmd)
 }
 }
 
 
 // POST /api/orgs/:orgId/users
 // POST /api/orgs/:orgId/users
-func AddOrgUser(c *middleware.Context, cmd m.AddOrgUserCommand) Response {
+func AddOrgUser(c *m.ReqContext, cmd m.AddOrgUserCommand) Response {
 	cmd.OrgId = c.ParamsInt64(":orgId")
 	cmd.OrgId = c.ParamsInt64(":orgId")
 	return addOrgUserHelper(cmd)
 	return addOrgUserHelper(cmd)
 }
 }
@@ -45,18 +44,18 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response {
 }
 }
 
 
 // GET /api/org/users
 // GET /api/org/users
-func GetOrgUsersForCurrentOrg(c *middleware.Context) Response {
+func GetOrgUsersForCurrentOrg(c *m.ReqContext) Response {
 	return getOrgUsersHelper(c.OrgId, c.Params("query"), c.ParamsInt("limit"))
 	return getOrgUsersHelper(c.OrgId, c.Params("query"), c.ParamsInt("limit"))
 }
 }
 
 
 // GET /api/orgs/:orgId/users
 // GET /api/orgs/:orgId/users
-func GetOrgUsers(c *middleware.Context) Response {
+func GetOrgUsers(c *m.ReqContext) Response {
 	return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
 	return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
 }
 }
 
 
-func getOrgUsersHelper(orgId int64, query string, limit int) Response {
+func getOrgUsersHelper(orgID int64, query string, limit int) Response {
 	q := m.GetOrgUsersQuery{
 	q := m.GetOrgUsersQuery{
-		OrgId: orgId,
+		OrgId: orgID,
 		Query: query,
 		Query: query,
 		Limit: limit,
 		Limit: limit,
 	}
 	}
@@ -73,14 +72,14 @@ func getOrgUsersHelper(orgId int64, query string, limit int) Response {
 }
 }
 
 
 // PATCH /api/org/users/:userId
 // PATCH /api/org/users/:userId
-func UpdateOrgUserForCurrentOrg(c *middleware.Context, cmd m.UpdateOrgUserCommand) Response {
+func UpdateOrgUserForCurrentOrg(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	cmd.UserId = c.ParamsInt64(":userId")
 	cmd.UserId = c.ParamsInt64(":userId")
 	return updateOrgUserHelper(cmd)
 	return updateOrgUserHelper(cmd)
 }
 }
 
 
 // PATCH /api/orgs/:orgId/users/:userId
 // PATCH /api/orgs/:orgId/users/:userId
-func UpdateOrgUser(c *middleware.Context, cmd m.UpdateOrgUserCommand) Response {
+func UpdateOrgUser(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response {
 	cmd.OrgId = c.ParamsInt64(":orgId")
 	cmd.OrgId = c.ParamsInt64(":orgId")
 	cmd.UserId = c.ParamsInt64(":userId")
 	cmd.UserId = c.ParamsInt64(":userId")
 	return updateOrgUserHelper(cmd)
 	return updateOrgUserHelper(cmd)
@@ -102,20 +101,20 @@ func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response {
 }
 }
 
 
 // DELETE /api/org/users/:userId
 // DELETE /api/org/users/:userId
-func RemoveOrgUserForCurrentOrg(c *middleware.Context) Response {
-	userId := c.ParamsInt64(":userId")
-	return removeOrgUserHelper(c.OrgId, userId)
+func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response {
+	userID := c.ParamsInt64(":userId")
+	return removeOrgUserHelper(c.OrgId, userID)
 }
 }
 
 
 // DELETE /api/orgs/:orgId/users/:userId
 // DELETE /api/orgs/:orgId/users/:userId
-func RemoveOrgUser(c *middleware.Context) Response {
-	userId := c.ParamsInt64(":userId")
-	orgId := c.ParamsInt64(":orgId")
-	return removeOrgUserHelper(orgId, userId)
+func RemoveOrgUser(c *m.ReqContext) Response {
+	userID := c.ParamsInt64(":userId")
+	orgID := c.ParamsInt64(":orgId")
+	return removeOrgUserHelper(orgID, userID)
 }
 }
 
 
-func removeOrgUserHelper(orgId int64, userId int64) Response {
-	cmd := m.RemoveOrgUserCommand{OrgId: orgId, UserId: userId}
+func removeOrgUserHelper(orgID int64, userID int64) Response {
+	cmd := m.RemoveOrgUserCommand{OrgId: orgID, UserId: userID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		if err == m.ErrLastOrgAdmin {
 		if err == m.ErrLastOrgAdmin {

+ 2 - 3
pkg/api/password.go

@@ -3,12 +3,11 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func SendResetPasswordEmail(c *middleware.Context, form dtos.SendResetPasswordEmailForm) Response {
+func SendResetPasswordEmail(c *m.ReqContext, form dtos.SendResetPasswordEmailForm) Response {
 	userQuery := m.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
 	userQuery := m.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
 
 
 	if err := bus.Dispatch(&userQuery); err != nil {
 	if err := bus.Dispatch(&userQuery); err != nil {
@@ -24,7 +23,7 @@ func SendResetPasswordEmail(c *middleware.Context, form dtos.SendResetPasswordEm
 	return ApiSuccess("Email sent")
 	return ApiSuccess("Email sent")
 }
 }
 
 
-func ResetPassword(c *middleware.Context, form dtos.ResetUserPasswordForm) Response {
+func ResetPassword(c *m.ReqContext, form dtos.ResetUserPasswordForm) Response {
 	query := m.ValidateResetPasswordCodeQuery{Code: form.Code}
 	query := m.ValidateResetPasswordCodeQuery{Code: form.Code}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 10 - 11
pkg/api/playlist.go

@@ -3,11 +3,10 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	_ "github.com/grafana/grafana/pkg/log"
 	_ "github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
-func ValidateOrgPlaylist(c *middleware.Context) {
+func ValidateOrgPlaylist(c *m.ReqContext) {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 	query := m.GetPlaylistByIdQuery{Id: id}
 	query := m.GetPlaylistByIdQuery{Id: id}
 	err := bus.Dispatch(&query)
 	err := bus.Dispatch(&query)
@@ -40,7 +39,7 @@ func ValidateOrgPlaylist(c *middleware.Context) {
 	}
 	}
 }
 }
 
 
-func SearchPlaylists(c *middleware.Context) Response {
+func SearchPlaylists(c *m.ReqContext) Response {
 	query := c.Query("query")
 	query := c.Query("query")
 	limit := c.QueryInt("limit")
 	limit := c.QueryInt("limit")
 
 
@@ -62,7 +61,7 @@ func SearchPlaylists(c *middleware.Context) Response {
 	return Json(200, searchQuery.Result)
 	return Json(200, searchQuery.Result)
 }
 }
 
 
-func GetPlaylist(c *middleware.Context) Response {
+func GetPlaylist(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 	cmd := m.GetPlaylistByIdQuery{Id: id}
 	cmd := m.GetPlaylistByIdQuery{Id: id}
 
 
@@ -115,7 +114,7 @@ func LoadPlaylistItems(id int64) ([]m.PlaylistItem, error) {
 	return *itemQuery.Result, nil
 	return *itemQuery.Result, nil
 }
 }
 
 
-func GetPlaylistItems(c *middleware.Context) Response {
+func GetPlaylistItems(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	playlistDTOs, err := LoadPlaylistItemDTOs(id)
 	playlistDTOs, err := LoadPlaylistItemDTOs(id)
@@ -127,10 +126,10 @@ func GetPlaylistItems(c *middleware.Context) Response {
 	return Json(200, playlistDTOs)
 	return Json(200, playlistDTOs)
 }
 }
 
 
-func GetPlaylistDashboards(c *middleware.Context) Response {
-	playlistId := c.ParamsInt64(":id")
+func GetPlaylistDashboards(c *m.ReqContext) Response {
+	playlistID := c.ParamsInt64(":id")
 
 
-	playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistId)
+	playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistID)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Could not load dashboards", err)
 		return ApiError(500, "Could not load dashboards", err)
 	}
 	}
@@ -138,7 +137,7 @@ func GetPlaylistDashboards(c *middleware.Context) Response {
 	return Json(200, playlists)
 	return Json(200, playlists)
 }
 }
 
 
-func DeletePlaylist(c *middleware.Context) Response {
+func DeletePlaylist(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	cmd := m.DeletePlaylistCommand{Id: id, OrgId: c.OrgId}
 	cmd := m.DeletePlaylistCommand{Id: id, OrgId: c.OrgId}
@@ -149,7 +148,7 @@ func DeletePlaylist(c *middleware.Context) Response {
 	return Json(200, "")
 	return Json(200, "")
 }
 }
 
 
-func CreatePlaylist(c *middleware.Context, cmd m.CreatePlaylistCommand) Response {
+func CreatePlaylist(c *m.ReqContext, cmd m.CreatePlaylistCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
@@ -159,7 +158,7 @@ func CreatePlaylist(c *middleware.Context, cmd m.CreatePlaylistCommand) Response
 	return Json(200, cmd.Result)
 	return Json(200, cmd.Result)
 }
 }
 
 
-func UpdatePlaylist(c *middleware.Context, cmd m.UpdatePlaylistCommand) Response {
+func UpdatePlaylist(c *m.ReqContext, cmd m.UpdatePlaylistCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {

+ 27 - 29
pkg/api/playlist_play.go

@@ -34,29 +34,27 @@ func populateDashboardsById(dashboardByIds []int64, dashboardIdOrder map[int64]i
 	return result, nil
 	return result, nil
 }
 }
 
 
-func populateDashboardsByTag(orgId int64, signedInUser *m.SignedInUser, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
+func populateDashboardsByTag(orgID int64, signedInUser *m.SignedInUser, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 
 
-	if len(dashboardByTag) > 0 {
-		for _, tag := range dashboardByTag {
-			searchQuery := search.Query{
-				Title:        "",
-				Tags:         []string{tag},
-				SignedInUser: signedInUser,
-				Limit:        100,
-				IsStarred:    false,
-				OrgId:        orgId,
-			}
+	for _, tag := range dashboardByTag {
+		searchQuery := search.Query{
+			Title:        "",
+			Tags:         []string{tag},
+			SignedInUser: signedInUser,
+			Limit:        100,
+			IsStarred:    false,
+			OrgId:        orgID,
+		}
 
 
-			if err := bus.Dispatch(&searchQuery); err == nil {
-				for _, item := range searchQuery.Result {
-					result = append(result, dtos.PlaylistDashboard{
-						Id:    item.Id,
-						Title: item.Title,
-						Uri:   item.Uri,
-						Order: dashboardTagOrder[tag],
-					})
-				}
+		if err := bus.Dispatch(&searchQuery); err == nil {
+			for _, item := range searchQuery.Result {
+				result = append(result, dtos.PlaylistDashboard{
+					Id:    item.Id,
+					Title: item.Title,
+					Uri:   item.Uri,
+					Order: dashboardTagOrder[tag],
+				})
 			}
 			}
 		}
 		}
 	}
 	}
@@ -64,19 +62,19 @@ func populateDashboardsByTag(orgId int64, signedInUser *m.SignedInUser, dashboar
 	return result
 	return result
 }
 }
 
 
-func LoadPlaylistDashboards(orgId int64, signedInUser *m.SignedInUser, playlistId int64) (dtos.PlaylistDashboardsSlice, error) {
-	playlistItems, _ := LoadPlaylistItems(playlistId)
+func LoadPlaylistDashboards(orgID int64, signedInUser *m.SignedInUser, playlistID int64) (dtos.PlaylistDashboardsSlice, error) {
+	playlistItems, _ := LoadPlaylistItems(playlistID)
 
 
-	dashboardByIds := make([]int64, 0)
+	dashboardByIDs := make([]int64, 0)
 	dashboardByTag := make([]string, 0)
 	dashboardByTag := make([]string, 0)
-	dashboardIdOrder := make(map[int64]int)
+	dashboardIDOrder := make(map[int64]int)
 	dashboardTagOrder := make(map[string]int)
 	dashboardTagOrder := make(map[string]int)
 
 
 	for _, i := range playlistItems {
 	for _, i := range playlistItems {
 		if i.Type == "dashboard_by_id" {
 		if i.Type == "dashboard_by_id" {
-			dashboardId, _ := strconv.ParseInt(i.Value, 10, 64)
-			dashboardByIds = append(dashboardByIds, dashboardId)
-			dashboardIdOrder[dashboardId] = i.Order
+			dashboardID, _ := strconv.ParseInt(i.Value, 10, 64)
+			dashboardByIDs = append(dashboardByIDs, dashboardID)
+			dashboardIDOrder[dashboardID] = i.Order
 		}
 		}
 
 
 		if i.Type == "dashboard_by_tag" {
 		if i.Type == "dashboard_by_tag" {
@@ -87,9 +85,9 @@ func LoadPlaylistDashboards(orgId int64, signedInUser *m.SignedInUser, playlistI
 
 
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 
 
-	var k, _ = populateDashboardsById(dashboardByIds, dashboardIdOrder)
+	var k, _ = populateDashboardsById(dashboardByIDs, dashboardIDOrder)
 	result = append(result, k...)
 	result = append(result, k...)
-	result = append(result, populateDashboardsByTag(orgId, signedInUser, dashboardByTag, dashboardTagOrder)...)
+	result = append(result, populateDashboardsByTag(orgID, signedInUser, dashboardByTag, dashboardTagOrder)...)
 
 
 	sort.Sort(result)
 	sort.Sort(result)
 	return result, nil
 	return result, nil

+ 11 - 6
pkg/api/pluginproxy/ds_proxy.go

@@ -18,7 +18,6 @@ import (
 	"github.com/opentracing/opentracing-go"
 	"github.com/opentracing/opentracing-go"
 
 
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
@@ -42,14 +41,14 @@ type jwtToken struct {
 
 
 type DataSourceProxy struct {
 type DataSourceProxy struct {
 	ds        *m.DataSource
 	ds        *m.DataSource
-	ctx       *middleware.Context
+	ctx       *m.ReqContext
 	targetUrl *url.URL
 	targetUrl *url.URL
 	proxyPath string
 	proxyPath string
 	route     *plugins.AppPluginRoute
 	route     *plugins.AppPluginRoute
 	plugin    *plugins.DataSourcePlugin
 	plugin    *plugins.DataSourcePlugin
 }
 }
 
 
-func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx *middleware.Context, proxyPath string) *DataSourceProxy {
+func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx *m.ReqContext, proxyPath string) *DataSourceProxy {
 	targetUrl, _ := url.Parse(ds.Url)
 	targetUrl, _ := url.Parse(ds.Url)
 
 
 	return &DataSourceProxy{
 	return &DataSourceProxy{
@@ -190,8 +189,14 @@ func (proxy *DataSourceProxy) validateRequest() error {
 	}
 	}
 
 
 	if proxy.ds.Type == m.DS_PROMETHEUS {
 	if proxy.ds.Type == m.DS_PROMETHEUS {
-		if proxy.ctx.Req.Request.Method != http.MethodGet || !strings.HasPrefix(proxy.proxyPath, "api/") {
-			return errors.New("GET is only allowed on proxied Prometheus datasource")
+		if proxy.ctx.Req.Request.Method == "DELETE" {
+			return errors.New("Deletes not allowed on proxied Prometheus datasource")
+		}
+		if proxy.ctx.Req.Request.Method == "PUT" {
+			return errors.New("Puts not allowed on proxied Prometheus datasource")
+		}
+		if proxy.ctx.Req.Request.Method == "POST" && !(proxy.proxyPath == "api/v1/query" || proxy.proxyPath == "api/v1/query_range") {
+			return errors.New("Posts not allowed on proxied Prometheus datasource except on /query and /query_range")
 		}
 		}
 	}
 	}
 
 
@@ -255,7 +260,7 @@ func (proxy *DataSourceProxy) logRequest() {
 		"body", body)
 		"body", body)
 }
 }
 
 
-func checkWhiteList(c *middleware.Context, host string) bool {
+func checkWhiteList(c *m.ReqContext, host string) bool {
 	if host != "" && len(setting.DataProxyWhiteList) > 0 {
 	if host != "" && len(setting.DataProxyWhiteList) > 0 {
 		if _, exists := setting.DataProxyWhiteList[host]; !exists {
 		if _, exists := setting.DataProxyWhiteList[host]; !exists {
 			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
 			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)

+ 5 - 6
pkg/api/pluginproxy/ds_proxy_test.go

@@ -8,7 +8,6 @@ import (
 	macaron "gopkg.in/macaron.v1"
 	macaron "gopkg.in/macaron.v1"
 
 
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
@@ -61,7 +60,7 @@ func TestDSRouteRule(t *testing.T) {
 			}
 			}
 
 
 			req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
 			req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
-			ctx := &middleware.Context{
+			ctx := &m.ReqContext{
 				Context: &macaron.Context{
 				Context: &macaron.Context{
 					Req: macaron.Request{Request: req},
 					Req: macaron.Request{Request: req},
 				},
 				},
@@ -104,7 +103,7 @@ func TestDSRouteRule(t *testing.T) {
 		Convey("When proxying graphite", func() {
 		Convey("When proxying graphite", func() {
 			plugin := &plugins.DataSourcePlugin{}
 			plugin := &plugins.DataSourcePlugin{}
 			ds := &m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
 			ds := &m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
-			ctx := &middleware.Context{}
+			ctx := &m.ReqContext{}
 
 
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "/render")
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "/render")
 
 
@@ -130,7 +129,7 @@ func TestDSRouteRule(t *testing.T) {
 				Password: "password",
 				Password: "password",
 			}
 			}
 
 
-			ctx := &middleware.Context{}
+			ctx := &m.ReqContext{}
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 
 
 			requestUrl, _ := url.Parse("http://grafana.com/sub")
 			requestUrl, _ := url.Parse("http://grafana.com/sub")
@@ -160,7 +159,7 @@ func TestDSRouteRule(t *testing.T) {
 				JsonData: json,
 				JsonData: json,
 			}
 			}
 
 
-			ctx := &middleware.Context{}
+			ctx := &m.ReqContext{}
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 
 
 			requestUrl, _ := url.Parse("http://grafana.com/sub")
 			requestUrl, _ := url.Parse("http://grafana.com/sub")
@@ -186,7 +185,7 @@ func TestDSRouteRule(t *testing.T) {
 				JsonData: json,
 				JsonData: json,
 			}
 			}
 
 
-			ctx := &middleware.Context{}
+			ctx := &m.ReqContext{}
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 			proxy := NewDataSourceProxy(ds, plugin, ctx, "")
 
 
 			requestUrl, _ := url.Parse("http://grafana.com/sub")
 			requestUrl, _ := url.Parse("http://grafana.com/sub")

+ 1 - 2
pkg/api/pluginproxy/pluginproxy.go

@@ -9,7 +9,6 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
@@ -38,7 +37,7 @@ func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.
 	return result, err
 	return result, err
 }
 }
 
 
-func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy {
+func NewApiPluginProxy(ctx *m.ReqContext, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy {
 	targetUrl, _ := url.Parse(route.Url)
 	targetUrl, _ := url.Parse(route.Url)
 
 
 	director := func(req *http.Request) {
 	director := func(req *http.Request) {

+ 48 - 47
pkg/api/plugins.go

@@ -5,13 +5,12 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
-func GetPluginList(c *middleware.Context) Response {
+func GetPluginList(c *m.ReqContext) Response {
 	typeFilter := c.Query("type")
 	typeFilter := c.Query("type")
 	enabledFilter := c.Query("enabled")
 	enabledFilter := c.Query("enabled")
 	embeddedFilter := c.Query("embedded")
 	embeddedFilter := c.Query("embedded")
@@ -79,48 +78,48 @@ func GetPluginList(c *middleware.Context) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetPluginSettingById(c *middleware.Context) Response {
-	pluginId := c.Params(":pluginId")
+func GetPluginSettingByID(c *m.ReqContext) Response {
+	pluginID := c.Params(":pluginId")
 
 
-	if def, exists := plugins.Plugins[pluginId]; !exists {
+	def, exists := plugins.Plugins[pluginID]
+	if !exists {
 		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
 		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
-	} else {
+	}
 
 
-		dto := &dtos.PluginSetting{
-			Type:          def.Type,
-			Id:            def.Id,
-			Name:          def.Name,
-			Info:          &def.Info,
-			Dependencies:  &def.Dependencies,
-			Includes:      def.Includes,
-			BaseUrl:       def.BaseUrl,
-			Module:        def.Module,
-			DefaultNavUrl: def.DefaultNavUrl,
-			LatestVersion: def.GrafanaNetVersion,
-			HasUpdate:     def.GrafanaNetHasUpdate,
-			State:         def.State,
-		}
+	dto := &dtos.PluginSetting{
+		Type:          def.Type,
+		Id:            def.Id,
+		Name:          def.Name,
+		Info:          &def.Info,
+		Dependencies:  &def.Dependencies,
+		Includes:      def.Includes,
+		BaseUrl:       def.BaseUrl,
+		Module:        def.Module,
+		DefaultNavUrl: def.DefaultNavUrl,
+		LatestVersion: def.GrafanaNetVersion,
+		HasUpdate:     def.GrafanaNetHasUpdate,
+		State:         def.State,
+	}
 
 
-		query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
-		if err := bus.Dispatch(&query); err != nil {
-			if err != m.ErrPluginSettingNotFound {
-				return ApiError(500, "Failed to get login settings", nil)
-			}
-		} else {
-			dto.Enabled = query.Result.Enabled
-			dto.Pinned = query.Result.Pinned
-			dto.JsonData = query.Result.JsonData
+	query := m.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
+	if err := bus.Dispatch(&query); err != nil {
+		if err != m.ErrPluginSettingNotFound {
+			return ApiError(500, "Failed to get login settings", nil)
 		}
 		}
-
-		return Json(200, dto)
+	} else {
+		dto.Enabled = query.Result.Enabled
+		dto.Pinned = query.Result.Pinned
+		dto.JsonData = query.Result.JsonData
 	}
 	}
+
+	return Json(200, dto)
 }
 }
 
 
-func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Response {
-	pluginId := c.Params(":pluginId")
+func UpdatePluginSetting(c *m.ReqContext, cmd m.UpdatePluginSettingCmd) Response {
+	pluginID := c.Params(":pluginId")
 
 
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
-	cmd.PluginId = pluginId
+	cmd.PluginId = pluginID
 
 
 	if _, ok := plugins.Apps[cmd.PluginId]; !ok {
 	if _, ok := plugins.Apps[cmd.PluginId]; !ok {
 		return ApiError(404, "Plugin not installed.", nil)
 		return ApiError(404, "Plugin not installed.", nil)
@@ -133,38 +132,40 @@ func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Re
 	return ApiSuccess("Plugin settings updated")
 	return ApiSuccess("Plugin settings updated")
 }
 }
 
 
-func GetPluginDashboards(c *middleware.Context) Response {
-	pluginId := c.Params(":pluginId")
+func GetPluginDashboards(c *m.ReqContext) Response {
+	pluginID := c.Params(":pluginId")
 
 
-	if list, err := plugins.GetPluginDashboards(c.OrgId, pluginId); err != nil {
+	list, err := plugins.GetPluginDashboards(c.OrgId, pluginID)
+	if err != nil {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 			return ApiError(404, notfound.Error(), nil)
 			return ApiError(404, notfound.Error(), nil)
 		}
 		}
 
 
 		return ApiError(500, "Failed to get plugin dashboards", err)
 		return ApiError(500, "Failed to get plugin dashboards", err)
-	} else {
-		return Json(200, list)
 	}
 	}
+
+	return Json(200, list)
 }
 }
 
 
-func GetPluginMarkdown(c *middleware.Context) Response {
-	pluginId := c.Params(":pluginId")
+func GetPluginMarkdown(c *m.ReqContext) Response {
+	pluginID := c.Params(":pluginId")
 	name := c.Params(":name")
 	name := c.Params(":name")
 
 
-	if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil {
+	content, err := plugins.GetPluginMarkdown(pluginID, name)
+	if err != nil {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 			return ApiError(404, notfound.Error(), nil)
 			return ApiError(404, notfound.Error(), nil)
 		}
 		}
 
 
 		return ApiError(500, "Could not get markdown file", err)
 		return ApiError(500, "Could not get markdown file", err)
-	} else {
-		resp := Respond(200, content)
-		resp.Header("Content-Type", "text/plain; charset=utf-8")
-		return resp
 	}
 	}
+
+	resp := Respond(200, content)
+	resp.Header("Content-Type", "text/plain; charset=utf-8")
+	return resp
 }
 }
 
 
-func ImportDashboard(c *middleware.Context, apiCmd dtos.ImportDashboardCommand) Response {
+func ImportDashboard(c *m.ReqContext, apiCmd dtos.ImportDashboardCommand) Response {
 
 
 	cmd := plugins.ImportDashboardCommand{
 	cmd := plugins.ImportDashboardCommand{
 		OrgId:     c.OrgId,
 		OrgId:     c.OrgId,

+ 10 - 11
pkg/api/preferences.go

@@ -3,12 +3,11 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
 // POST /api/preferences/set-home-dash
 // POST /api/preferences/set-home-dash
-func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response {
+func SetHomeDashboard(c *m.ReqContext, cmd m.SavePreferencesCommand) Response {
 
 
 	cmd.UserId = c.UserId
 	cmd.UserId = c.UserId
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
@@ -21,12 +20,12 @@ func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Respo
 }
 }
 
 
 // GET /api/user/preferences
 // GET /api/user/preferences
-func GetUserPreferences(c *middleware.Context) Response {
+func GetUserPreferences(c *m.ReqContext) Response {
 	return getPreferencesFor(c.OrgId, c.UserId)
 	return getPreferencesFor(c.OrgId, c.UserId)
 }
 }
 
 
-func getPreferencesFor(orgId int64, userId int64) Response {
-	prefsQuery := m.GetPreferencesQuery{UserId: userId, OrgId: orgId}
+func getPreferencesFor(orgID int64, userID int64) Response {
+	prefsQuery := m.GetPreferencesQuery{UserId: userID, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 		return ApiError(500, "Failed to get preferences", err)
 		return ApiError(500, "Failed to get preferences", err)
@@ -42,14 +41,14 @@ func getPreferencesFor(orgId int64, userId int64) Response {
 }
 }
 
 
 // PUT /api/user/preferences
 // PUT /api/user/preferences
-func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
+func UpdateUserPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
 	return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
 	return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
 }
 }
 
 
-func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
+func updatePreferencesFor(orgID int64, userID int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
 	saveCmd := m.SavePreferencesCommand{
 	saveCmd := m.SavePreferencesCommand{
-		UserId:          userId,
-		OrgId:           orgId,
+		UserId:          userID,
+		OrgId:           orgID,
 		Theme:           dtoCmd.Theme,
 		Theme:           dtoCmd.Theme,
 		Timezone:        dtoCmd.Timezone,
 		Timezone:        dtoCmd.Timezone,
 		HomeDashboardId: dtoCmd.HomeDashboardId,
 		HomeDashboardId: dtoCmd.HomeDashboardId,
@@ -63,11 +62,11 @@ func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd
 }
 }
 
 
 // GET /api/org/preferences
 // GET /api/org/preferences
-func GetOrgPreferences(c *middleware.Context) Response {
+func GetOrgPreferences(c *m.ReqContext) Response {
 	return getPreferencesFor(c.OrgId, 0)
 	return getPreferencesFor(c.OrgId, 0)
 }
 }
 
 
 // PUT /api/org/preferences
 // PUT /api/org/preferences
-func UpdateOrgPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
+func UpdateOrgPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
 	return updatePreferencesFor(c.OrgId, 0, &dtoCmd)
 	return updatePreferencesFor(c.OrgId, 0, &dtoCmd)
 }
 }

+ 4 - 5
pkg/api/quota.go

@@ -2,12 +2,11 @@ package api
 
 
 import (
 import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
-func GetOrgQuotas(c *middleware.Context) Response {
+func GetOrgQuotas(c *m.ReqContext) Response {
 	if !setting.Quota.Enabled {
 	if !setting.Quota.Enabled {
 		return ApiError(404, "Quotas not enabled", nil)
 		return ApiError(404, "Quotas not enabled", nil)
 	}
 	}
@@ -20,7 +19,7 @@ func GetOrgQuotas(c *middleware.Context) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func UpdateOrgQuota(c *middleware.Context, cmd m.UpdateOrgQuotaCmd) Response {
+func UpdateOrgQuota(c *m.ReqContext, cmd m.UpdateOrgQuotaCmd) Response {
 	if !setting.Quota.Enabled {
 	if !setting.Quota.Enabled {
 		return ApiError(404, "Quotas not enabled", nil)
 		return ApiError(404, "Quotas not enabled", nil)
 	}
 	}
@@ -37,7 +36,7 @@ func UpdateOrgQuota(c *middleware.Context, cmd m.UpdateOrgQuotaCmd) Response {
 	return ApiSuccess("Organization quota updated")
 	return ApiSuccess("Organization quota updated")
 }
 }
 
 
-func GetUserQuotas(c *middleware.Context) Response {
+func GetUserQuotas(c *m.ReqContext) Response {
 	if !setting.Quota.Enabled {
 	if !setting.Quota.Enabled {
 		return ApiError(404, "Quotas not enabled", nil)
 		return ApiError(404, "Quotas not enabled", nil)
 	}
 	}
@@ -50,7 +49,7 @@ func GetUserQuotas(c *middleware.Context) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func UpdateUserQuota(c *middleware.Context, cmd m.UpdateUserQuotaCmd) Response {
+func UpdateUserQuota(c *m.ReqContext, cmd m.UpdateUserQuotaCmd) Response {
 	if !setting.Quota.Enabled {
 	if !setting.Quota.Enabled {
 		return ApiError(404, "Quotas not enabled", nil)
 		return ApiError(404, "Quotas not enabled", nil)
 	}
 	}

+ 2 - 2
pkg/api/render.go

@@ -5,11 +5,11 @@ import (
 	"net/http"
 	"net/http"
 
 
 	"github.com/grafana/grafana/pkg/components/renderer"
 	"github.com/grafana/grafana/pkg/components/renderer"
-	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func RenderToPng(c *middleware.Context) {
+func RenderToPng(c *m.ReqContext) {
 	queryReader, err := util.NewUrlQueryReader(c.Req.URL)
 	queryReader, err := util.NewUrlQueryReader(c.Req.URL)
 	if err != nil {
 	if err != nil {
 		c.Handle(400, "Render parameters error", err)
 		c.Handle(400, "Render parameters error", err)

+ 12 - 13
pkg/api/search.go

@@ -5,40 +5,39 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/models"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/search"
 )
 )
 
 
-func Search(c *middleware.Context) {
+func Search(c *m.ReqContext) {
 	query := c.Query("query")
 	query := c.Query("query")
 	tags := c.QueryStrings("tag")
 	tags := c.QueryStrings("tag")
 	starred := c.Query("starred")
 	starred := c.Query("starred")
 	limit := c.QueryInt("limit")
 	limit := c.QueryInt("limit")
 	dashboardType := c.Query("type")
 	dashboardType := c.Query("type")
-	permission := models.PERMISSION_VIEW
+	permission := m.PERMISSION_VIEW
 
 
 	if limit == 0 {
 	if limit == 0 {
 		limit = 1000
 		limit = 1000
 	}
 	}
 
 
 	if c.Query("permission") == "Edit" {
 	if c.Query("permission") == "Edit" {
-		permission = models.PERMISSION_EDIT
+		permission = m.PERMISSION_EDIT
 	}
 	}
 
 
-	dbids := make([]int64, 0)
+	dbIDs := make([]int64, 0)
 	for _, id := range c.QueryStrings("dashboardIds") {
 	for _, id := range c.QueryStrings("dashboardIds") {
-		dashboardId, err := strconv.ParseInt(id, 10, 64)
+		dashboardID, err := strconv.ParseInt(id, 10, 64)
 		if err == nil {
 		if err == nil {
-			dbids = append(dbids, dashboardId)
+			dbIDs = append(dbIDs, dashboardID)
 		}
 		}
 	}
 	}
 
 
-	folderIds := make([]int64, 0)
+	folderIDs := make([]int64, 0)
 	for _, id := range c.QueryStrings("folderIds") {
 	for _, id := range c.QueryStrings("folderIds") {
-		folderId, err := strconv.ParseInt(id, 10, 64)
+		folderID, err := strconv.ParseInt(id, 10, 64)
 		if err == nil {
 		if err == nil {
-			folderIds = append(folderIds, folderId)
+			folderIDs = append(folderIDs, folderID)
 		}
 		}
 	}
 	}
 
 
@@ -49,9 +48,9 @@ func Search(c *middleware.Context) {
 		Limit:        limit,
 		Limit:        limit,
 		IsStarred:    starred == "true",
 		IsStarred:    starred == "true",
 		OrgId:        c.OrgId,
 		OrgId:        c.OrgId,
-		DashboardIds: dbids,
+		DashboardIds: dbIDs,
 		Type:         dashboardType,
 		Type:         dashboardType,
-		FolderIds:    folderIds,
+		FolderIds:    folderIDs,
 		Permission:   permission,
 		Permission:   permission,
 	}
 	}
 
 

+ 3 - 4
pkg/api/signup.go

@@ -5,14 +5,13 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // GET /api/user/signup/options
 // GET /api/user/signup/options
-func GetSignUpOptions(c *middleware.Context) Response {
+func GetSignUpOptions(c *m.ReqContext) Response {
 	return Json(200, util.DynMap{
 	return Json(200, util.DynMap{
 		"verifyEmailEnabled": setting.VerifyEmailEnabled,
 		"verifyEmailEnabled": setting.VerifyEmailEnabled,
 		"autoAssignOrg":      setting.AutoAssignOrg,
 		"autoAssignOrg":      setting.AutoAssignOrg,
@@ -20,7 +19,7 @@ func GetSignUpOptions(c *middleware.Context) Response {
 }
 }
 
 
 // POST /api/user/signup
 // POST /api/user/signup
-func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
+func SignUp(c *m.ReqContext, form dtos.SignUpForm) Response {
 	if !setting.AllowUserSignUp {
 	if !setting.AllowUserSignUp {
 		return ApiError(401, "User signup is disabled", nil)
 		return ApiError(401, "User signup is disabled", nil)
 	}
 	}
@@ -52,7 +51,7 @@ func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
 	return Json(200, util.DynMap{"status": "SignUpCreated"})
 	return Json(200, util.DynMap{"status": "SignUpCreated"})
 }
 }
 
 
-func SignUpStep2(c *middleware.Context, form dtos.SignUpStep2Form) Response {
+func SignUpStep2(c *m.ReqContext, form dtos.SignUpStep2Form) Response {
 	if !setting.AllowUserSignUp {
 	if !setting.AllowUserSignUp {
 		return ApiError(401, "User signup is disabled", nil)
 		return ApiError(401, "User signup is disabled", nil)
 	}
 	}

+ 2 - 3
pkg/api/stars.go

@@ -2,11 +2,10 @@ package api
 
 
 import (
 import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
-func StarDashboard(c *middleware.Context) Response {
+func StarDashboard(c *m.ReqContext) Response {
 	if !c.IsSignedIn {
 	if !c.IsSignedIn {
 		return ApiError(412, "You need to sign in to star dashboards", nil)
 		return ApiError(412, "You need to sign in to star dashboards", nil)
 	}
 	}
@@ -24,7 +23,7 @@ func StarDashboard(c *middleware.Context) Response {
 	return ApiSuccess("Dashboard starred!")
 	return ApiSuccess("Dashboard starred!")
 }
 }
 
 
-func UnstarDashboard(c *middleware.Context) Response {
+func UnstarDashboard(c *m.ReqContext) Response {
 
 
 	cmd := m.UnstarDashboardCommand{UserId: c.UserId, DashboardId: c.ParamsInt64(":id")}
 	cmd := m.UnstarDashboardCommand{UserId: c.UserId, DashboardId: c.ParamsInt64(":id")}
 
 

+ 5 - 6
pkg/api/team.go

@@ -3,13 +3,12 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // POST /api/teams
 // POST /api/teams
-func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
+func CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		if err == m.ErrTeamNameTaken {
 		if err == m.ErrTeamNameTaken {
@@ -25,7 +24,7 @@ func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
 }
 }
 
 
 // PUT /api/teams/:teamId
 // PUT /api/teams/:teamId
-func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
+func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	cmd.Id = c.ParamsInt64(":teamId")
 	cmd.Id = c.ParamsInt64(":teamId")
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
@@ -39,7 +38,7 @@ func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
 }
 }
 
 
 // DELETE /api/teams/:teamId
 // DELETE /api/teams/:teamId
-func DeleteTeamById(c *middleware.Context) Response {
+func DeleteTeamByID(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil {
 	if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil {
 		if err == m.ErrTeamNotFound {
 		if err == m.ErrTeamNotFound {
 			return ApiError(404, "Failed to delete Team. ID not found", nil)
 			return ApiError(404, "Failed to delete Team. ID not found", nil)
@@ -50,7 +49,7 @@ func DeleteTeamById(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/teams/search
 // GET /api/teams/search
-func SearchTeams(c *middleware.Context) Response {
+func SearchTeams(c *m.ReqContext) Response {
 	perPage := c.QueryInt("perpage")
 	perPage := c.QueryInt("perpage")
 	if perPage <= 0 {
 	if perPage <= 0 {
 		perPage = 1000
 		perPage = 1000
@@ -83,7 +82,7 @@ func SearchTeams(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/teams/:teamId
 // GET /api/teams/:teamId
-func GetTeamById(c *middleware.Context) Response {
+func GetTeamByID(c *m.ReqContext) Response {
 	query := m.GetTeamByIdQuery{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}
 	query := m.GetTeamByIdQuery{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 3 - 4
pkg/api/team_members.go

@@ -3,13 +3,12 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // GET /api/teams/:teamId/members
 // GET /api/teams/:teamId/members
-func GetTeamMembers(c *middleware.Context) Response {
+func GetTeamMembers(c *m.ReqContext) Response {
 	query := m.GetTeamMembersQuery{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId")}
 	query := m.GetTeamMembersQuery{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId")}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -24,7 +23,7 @@ func GetTeamMembers(c *middleware.Context) Response {
 }
 }
 
 
 // POST /api/teams/:teamId/members
 // POST /api/teams/:teamId/members
-func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
+func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response {
 	cmd.TeamId = c.ParamsInt64(":teamId")
 	cmd.TeamId = c.ParamsInt64(":teamId")
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 
 
@@ -46,7 +45,7 @@ func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
 }
 }
 
 
 // DELETE /api/teams/:teamId/members/:userId
 // DELETE /api/teams/:teamId/members/:userId
-func RemoveTeamMember(c *middleware.Context) Response {
+func RemoveTeamMember(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil {
 	if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil {
 		if err == m.ErrTeamNotFound {
 		if err == m.ErrTeamNotFound {
 			return ApiError(404, "Team not found", nil)
 			return ApiError(404, "Team not found", nil)

+ 33 - 34
pkg/api/user.go

@@ -3,24 +3,23 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
 // GET /api/user  (current authenticated user)
 // GET /api/user  (current authenticated user)
-func GetSignedInUser(c *middleware.Context) Response {
+func GetSignedInUser(c *m.ReqContext) Response {
 	return getUserUserProfile(c.UserId)
 	return getUserUserProfile(c.UserId)
 }
 }
 
 
 // GET /api/users/:id
 // GET /api/users/:id
-func GetUserById(c *middleware.Context) Response {
+func GetUserByID(c *m.ReqContext) Response {
 	return getUserUserProfile(c.ParamsInt64(":id"))
 	return getUserUserProfile(c.ParamsInt64(":id"))
 }
 }
 
 
-func getUserUserProfile(userId int64) Response {
-	query := m.GetUserProfileQuery{UserId: userId}
+func getUserUserProfile(userID int64) Response {
+	query := m.GetUserProfileQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		if err == m.ErrUserNotFound {
 		if err == m.ErrUserNotFound {
@@ -33,7 +32,7 @@ func getUserUserProfile(userId int64) Response {
 }
 }
 
 
 // GET /api/users/lookup
 // GET /api/users/lookup
-func GetUserByLoginOrEmail(c *middleware.Context) Response {
+func GetUserByLoginOrEmail(c *m.ReqContext) Response {
 	query := m.GetUserByLoginQuery{LoginOrEmail: c.Query("loginOrEmail")}
 	query := m.GetUserByLoginQuery{LoginOrEmail: c.Query("loginOrEmail")}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		if err == m.ErrUserNotFound {
 		if err == m.ErrUserNotFound {
@@ -55,7 +54,7 @@ func GetUserByLoginOrEmail(c *middleware.Context) Response {
 }
 }
 
 
 // POST /api/user
 // POST /api/user
-func UpdateSignedInUser(c *middleware.Context, cmd m.UpdateUserCommand) Response {
+func UpdateSignedInUser(c *m.ReqContext, cmd m.UpdateUserCommand) Response {
 	if setting.AuthProxyEnabled {
 	if setting.AuthProxyEnabled {
 		if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
 		if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
 			return ApiError(400, "Not allowed to change email when auth proxy is using email property", nil)
 			return ApiError(400, "Not allowed to change email when auth proxy is using email property", nil)
@@ -69,21 +68,21 @@ func UpdateSignedInUser(c *middleware.Context, cmd m.UpdateUserCommand) Response
 }
 }
 
 
 // POST /api/users/:id
 // POST /api/users/:id
-func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) Response {
+func UpdateUser(c *m.ReqContext, cmd m.UpdateUserCommand) Response {
 	cmd.UserId = c.ParamsInt64(":id")
 	cmd.UserId = c.ParamsInt64(":id")
 	return handleUpdateUser(cmd)
 	return handleUpdateUser(cmd)
 }
 }
 
 
 //POST /api/users/:id/using/:orgId
 //POST /api/users/:id/using/:orgId
-func UpdateUserActiveOrg(c *middleware.Context) Response {
-	userId := c.ParamsInt64(":id")
-	orgId := c.ParamsInt64(":orgId")
+func UpdateUserActiveOrg(c *m.ReqContext) Response {
+	userID := c.ParamsInt64(":id")
+	orgID := c.ParamsInt64(":orgId")
 
 
-	if !validateUsingOrg(userId, orgId) {
+	if !validateUsingOrg(userID, orgID) {
 		return ApiError(401, "Not a valid organization", nil)
 		return ApiError(401, "Not a valid organization", nil)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: userId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: userID, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		return ApiError(500, "Failed to change active organization", err)
 		return ApiError(500, "Failed to change active organization", err)
@@ -108,17 +107,17 @@ func handleUpdateUser(cmd m.UpdateUserCommand) Response {
 }
 }
 
 
 // GET /api/user/orgs
 // GET /api/user/orgs
-func GetSignedInUserOrgList(c *middleware.Context) Response {
+func GetSignedInUserOrgList(c *m.ReqContext) Response {
 	return getUserOrgList(c.UserId)
 	return getUserOrgList(c.UserId)
 }
 }
 
 
 // GET /api/user/:id/orgs
 // GET /api/user/:id/orgs
-func GetUserOrgList(c *middleware.Context) Response {
+func GetUserOrgList(c *m.ReqContext) Response {
 	return getUserOrgList(c.ParamsInt64(":id"))
 	return getUserOrgList(c.ParamsInt64(":id"))
 }
 }
 
 
-func getUserOrgList(userId int64) Response {
-	query := m.GetUserOrgListQuery{UserId: userId}
+func getUserOrgList(userID int64) Response {
+	query := m.GetUserOrgListQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return ApiError(500, "Failed to get user organizations", err)
 		return ApiError(500, "Failed to get user organizations", err)
@@ -127,8 +126,8 @@ func getUserOrgList(userId int64) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func validateUsingOrg(userId int64, orgId int64) bool {
-	query := m.GetUserOrgListQuery{UserId: userId}
+func validateUsingOrg(userID int64, orgID int64) bool {
+	query := m.GetUserOrgListQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return false
 		return false
@@ -137,7 +136,7 @@ func validateUsingOrg(userId int64, orgId int64) bool {
 	// validate that the org id in the list
 	// validate that the org id in the list
 	valid := false
 	valid := false
 	for _, other := range query.Result {
 	for _, other := range query.Result {
-		if other.OrgId == orgId {
+		if other.OrgId == orgID {
 			valid = true
 			valid = true
 		}
 		}
 	}
 	}
@@ -146,14 +145,14 @@ func validateUsingOrg(userId int64, orgId int64) bool {
 }
 }
 
 
 // POST /api/user/using/:id
 // POST /api/user/using/:id
-func UserSetUsingOrg(c *middleware.Context) Response {
-	orgId := c.ParamsInt64(":id")
+func UserSetUsingOrg(c *m.ReqContext) Response {
+	orgID := c.ParamsInt64(":id")
 
 
-	if !validateUsingOrg(c.UserId, orgId) {
+	if !validateUsingOrg(c.UserId, orgID) {
 		return ApiError(401, "Not a valid organization", nil)
 		return ApiError(401, "Not a valid organization", nil)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		return ApiError(500, "Failed to change active organization", err)
 		return ApiError(500, "Failed to change active organization", err)
@@ -163,14 +162,14 @@ func UserSetUsingOrg(c *middleware.Context) Response {
 }
 }
 
 
 // GET /profile/switch-org/:id
 // GET /profile/switch-org/:id
-func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) {
-	orgId := c.ParamsInt64(":id")
+func ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) {
+	orgID := c.ParamsInt64(":id")
 
 
-	if !validateUsingOrg(c.UserId, orgId) {
+	if !validateUsingOrg(c.UserId, orgID) {
 		NotFoundHandler(c)
 		NotFoundHandler(c)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		NotFoundHandler(c)
 		NotFoundHandler(c)
@@ -179,7 +178,7 @@ func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) {
 	c.Redirect(setting.AppSubUrl + "/")
 	c.Redirect(setting.AppSubUrl + "/")
 }
 }
 
 
-func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response {
+func ChangeUserPassword(c *m.ReqContext, cmd m.ChangeUserPasswordCommand) Response {
 	if setting.LdapEnabled || setting.AuthProxyEnabled {
 	if setting.LdapEnabled || setting.AuthProxyEnabled {
 		return ApiError(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
 		return ApiError(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
 	}
 	}
@@ -211,7 +210,7 @@ func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand)
 }
 }
 
 
 // GET /api/users
 // GET /api/users
-func SearchUsers(c *middleware.Context) Response {
+func SearchUsers(c *m.ReqContext) Response {
 	query, err := searchUser(c)
 	query, err := searchUser(c)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Failed to fetch users", err)
 		return ApiError(500, "Failed to fetch users", err)
@@ -221,7 +220,7 @@ func SearchUsers(c *middleware.Context) Response {
 }
 }
 
 
 // GET /api/users/search
 // GET /api/users/search
-func SearchUsersWithPaging(c *middleware.Context) Response {
+func SearchUsersWithPaging(c *m.ReqContext) Response {
 	query, err := searchUser(c)
 	query, err := searchUser(c)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Failed to fetch users", err)
 		return ApiError(500, "Failed to fetch users", err)
@@ -230,7 +229,7 @@ func SearchUsersWithPaging(c *middleware.Context) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func searchUser(c *middleware.Context) (*m.SearchUsersQuery, error) {
+func searchUser(c *m.ReqContext) (*m.SearchUsersQuery, error) {
 	perPage := c.QueryInt("perpage")
 	perPage := c.QueryInt("perpage")
 	if perPage <= 0 {
 	if perPage <= 0 {
 		perPage = 1000
 		perPage = 1000
@@ -258,7 +257,7 @@ func searchUser(c *middleware.Context) (*m.SearchUsersQuery, error) {
 	return query, nil
 	return query, nil
 }
 }
 
 
-func SetHelpFlag(c *middleware.Context) Response {
+func SetHelpFlag(c *m.ReqContext) Response {
 	flag := c.ParamsInt64(":id")
 	flag := c.ParamsInt64(":id")
 
 
 	bitmask := &c.HelpFlags1
 	bitmask := &c.HelpFlags1
@@ -276,7 +275,7 @@ func SetHelpFlag(c *middleware.Context) Response {
 	return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
 	return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
 }
 }
 
 
-func ClearHelpFlags(c *middleware.Context) Response {
+func ClearHelpFlags(c *m.ReqContext) Response {
 	cmd := m.SetUserHelpFlagCommand{
 	cmd := m.SetUserHelpFlagCommand{
 		UserId:     c.UserId,
 		UserId:     c.UserId,
 		HelpFlags1: m.HelpFlags1(0),
 		HelpFlags1: m.HelpFlags1(0),

+ 1 - 1
pkg/cmd/grafana-server/server.go

@@ -120,7 +120,7 @@ func (g *GrafanaServerImpl) initLogging() {
 }
 }
 
 
 func (g *GrafanaServerImpl) startHttpServer() error {
 func (g *GrafanaServerImpl) startHttpServer() error {
-	g.httpServer = api.NewHttpServer()
+	g.httpServer = api.NewHTTPServer()
 
 
 	err := g.httpServer.Start(g.context)
 	err := g.httpServer.Start(g.context)
 
 

+ 3 - 1
pkg/components/renderer/renderer.go

@@ -72,7 +72,9 @@ func RenderToPng(params *RenderOpts) (string, error) {
 		localDomain = setting.HttpAddr
 		localDomain = setting.HttpAddr
 	}
 	}
 
 
-	url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localDomain, setting.HttpPort, params.Path)
+	// &render=1 signals to the legacy redirect layer to
+	// avoid redirect these requests.
+	url := fmt.Sprintf("%s://%s:%s/%s&render=1", setting.Protocol, localDomain, setting.HttpPort, params.Path)
 
 
 	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable))
 	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable))
 	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
 	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))

+ 10 - 9
pkg/middleware/auth.go

@@ -7,6 +7,7 @@ import (
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 
 
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
@@ -15,17 +16,17 @@ type AuthOptions struct {
 	ReqSignedIn     bool
 	ReqSignedIn     bool
 }
 }
 
 
-func getRequestUserId(c *Context) int64 {
-	userId := c.Session.Get(SESS_KEY_USERID)
+func getRequestUserId(c *m.ReqContext) int64 {
+	userID := c.Session.Get(session.SESS_KEY_USERID)
 
 
-	if userId != nil {
-		return userId.(int64)
+	if userID != nil {
+		return userID.(int64)
 	}
 	}
 
 
 	return 0
 	return 0
 }
 }
 
 
-func getApiKey(c *Context) string {
+func getApiKey(c *m.ReqContext) string {
 	header := c.Req.Header.Get("Authorization")
 	header := c.Req.Header.Get("Authorization")
 	parts := strings.SplitN(header, " ", 2)
 	parts := strings.SplitN(header, " ", 2)
 	if len(parts) == 2 && parts[0] == "Bearer" {
 	if len(parts) == 2 && parts[0] == "Bearer" {
@@ -36,7 +37,7 @@ func getApiKey(c *Context) string {
 	return ""
 	return ""
 }
 }
 
 
-func accessForbidden(c *Context) {
+func accessForbidden(c *m.ReqContext) {
 	if c.IsApiRequest() {
 	if c.IsApiRequest() {
 		c.JsonApiErr(403, "Permission denied", nil)
 		c.JsonApiErr(403, "Permission denied", nil)
 		return
 		return
@@ -45,7 +46,7 @@ func accessForbidden(c *Context) {
 	c.Redirect(setting.AppSubUrl + "/")
 	c.Redirect(setting.AppSubUrl + "/")
 }
 }
 
 
-func notAuthorized(c *Context) {
+func notAuthorized(c *m.ReqContext) {
 	if c.IsApiRequest() {
 	if c.IsApiRequest() {
 		c.JsonApiErr(401, "Unauthorized", nil)
 		c.JsonApiErr(401, "Unauthorized", nil)
 		return
 		return
@@ -57,7 +58,7 @@ func notAuthorized(c *Context) {
 }
 }
 
 
 func RoleAuth(roles ...m.RoleType) macaron.Handler {
 func RoleAuth(roles ...m.RoleType) macaron.Handler {
-	return func(c *Context) {
+	return func(c *m.ReqContext) {
 		ok := false
 		ok := false
 		for _, role := range roles {
 		for _, role := range roles {
 			if role == c.OrgRole {
 			if role == c.OrgRole {
@@ -72,7 +73,7 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler {
 }
 }
 
 
 func Auth(options *AuthOptions) macaron.Handler {
 func Auth(options *AuthOptions) macaron.Handler {
-	return func(c *Context) {
+	return func(c *m.ReqContext) {
 		if !c.IsSignedIn && options.ReqSignedIn && !c.AllowAnonymous {
 		if !c.IsSignedIn && options.ReqSignedIn && !c.AllowAnonymous {
 			notAuthorized(c)
 			notAuthorized(c)
 			return
 			return

+ 10 - 9
pkg/middleware/auth_proxy.go

@@ -10,10 +10,11 @@ import (
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )
 
 
-func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
+func initContextWithAuthProxy(ctx *m.ReqContext, orgId int64) bool {
 	if !setting.AuthProxyEnabled {
 	if !setting.AuthProxyEnabled {
 		return false
 		return false
 	}
 	}
@@ -58,7 +59,7 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
 	}
 	}
 
 
 	// initialize session
 	// initialize session
-	if err := ctx.Session.Start(ctx); err != nil {
+	if err := ctx.Session.Start(ctx.Context); err != nil {
 		log.Error(3, "Failed to start session", err)
 		log.Error(3, "Failed to start session", err)
 		return false
 		return false
 	}
 	}
@@ -66,12 +67,12 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
 	// Make sure that we cannot share a session between different users!
 	// Make sure that we cannot share a session between different users!
 	if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
 	if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
 		// remove session
 		// remove session
-		if err := ctx.Session.Destory(ctx); err != nil {
+		if err := ctx.Session.Destory(ctx.Context); err != nil {
 			log.Error(3, "Failed to destroy session, err")
 			log.Error(3, "Failed to destroy session, err")
 		}
 		}
 
 
 		// initialize a new session
 		// initialize a new session
-		if err := ctx.Session.Start(ctx); err != nil {
+		if err := ctx.Session.Start(ctx.Context); err != nil {
 			log.Error(3, "Failed to start session", err)
 			log.Error(3, "Failed to start session", err)
 		}
 		}
 	}
 	}
@@ -89,17 +90,17 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
 
 
 	ctx.SignedInUser = query.Result
 	ctx.SignedInUser = query.Result
 	ctx.IsSignedIn = true
 	ctx.IsSignedIn = true
-	ctx.Session.Set(SESS_KEY_USERID, ctx.UserId)
+	ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
 
 
 	return true
 	return true
 }
 }
 
 
-var syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQuery) error {
+var syncGrafanaUserWithLdapUser = func(ctx *m.ReqContext, query *m.GetSignedInUserQuery) error {
 	if setting.LdapEnabled {
 	if setting.LdapEnabled {
 		expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
 		expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
 
 
 		var lastLdapSync int64
 		var lastLdapSync int64
-		if lastLdapSyncInSession := ctx.Session.Get(SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
+		if lastLdapSyncInSession := ctx.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
 			lastLdapSync = lastLdapSyncInSession.(int64)
 			lastLdapSync = lastLdapSyncInSession.(int64)
 		}
 		}
 
 
@@ -113,14 +114,14 @@ var syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQue
 				}
 				}
 			}
 			}
 
 
-			ctx.Session.Set(SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
+			ctx.Session.Set(session.SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
 		}
 		}
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func checkAuthenticationProxy(ctx *Context, proxyHeaderValue string) error {
+func checkAuthenticationProxy(ctx *m.ReqContext, proxyHeaderValue string) error {
 	if len(strings.TrimSpace(setting.AuthProxyWhitelist)) > 0 {
 	if len(strings.TrimSpace(setting.AuthProxyWhitelist)) > 0 {
 		proxies := strings.Split(setting.AuthProxyWhitelist, ",")
 		proxies := strings.Split(setting.AuthProxyWhitelist, ",")
 		remoteAddrSplit := strings.Split(ctx.Req.RemoteAddr, ":")
 		remoteAddrSplit := strings.Split(ctx.Req.RemoteAddr, ":")

+ 17 - 15
pkg/middleware/auth_proxy_test.go

@@ -6,8 +6,10 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
+	"gopkg.in/macaron.v1"
 )
 )
 
 
 func TestAuthProxyWithLdapEnabled(t *testing.T) {
 func TestAuthProxyWithLdapEnabled(t *testing.T) {
@@ -29,45 +31,45 @@ func TestAuthProxyWithLdapEnabled(t *testing.T) {
 
 
 		Convey("When session variable lastLdapSync not set, call syncSignedInUser and set lastLdapSync", func() {
 		Convey("When session variable lastLdapSync not set, call syncSignedInUser and set lastLdapSync", func() {
 			// arrange
 			// arrange
-			session := mockSession{}
-			ctx := Context{Session: &session}
-			So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
+			sess := mockSession{}
+			ctx := m.ReqContext{Session: &sess}
+			So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
 
 
 			// act
 			// act
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 
 
 			// assert
 			// assert
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
-			So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
+			So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
 		})
 		})
 
 
 		Convey("When session variable not expired, don't sync and don't change session var", func() {
 		Convey("When session variable not expired, don't sync and don't change session var", func() {
 			// arrange
 			// arrange
-			session := mockSession{}
-			ctx := Context{Session: &session}
+			sess := mockSession{}
+			ctx := m.ReqContext{Session: &sess}
 			now := time.Now().Unix()
 			now := time.Now().Unix()
-			session.Set(SESS_KEY_LASTLDAPSYNC, now)
+			sess.Set(session.SESS_KEY_LASTLDAPSYNC, now)
 
 
 			// act
 			// act
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 
 
 			// assert
 			// assert
-			So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
+			So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeFalse)
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeFalse)
 		})
 		})
 
 
 		Convey("When lastldapsync is expired, session variable should be updated", func() {
 		Convey("When lastldapsync is expired, session variable should be updated", func() {
 			// arrange
 			// arrange
-			session := mockSession{}
-			ctx := Context{Session: &session}
+			sess := mockSession{}
+			ctx := m.ReqContext{Session: &sess}
 			expiredTime := time.Now().Add(time.Duration(-120) * time.Minute).Unix()
 			expiredTime := time.Now().Add(time.Duration(-120) * time.Minute).Unix()
-			session.Set(SESS_KEY_LASTLDAPSYNC, expiredTime)
+			sess.Set(session.SESS_KEY_LASTLDAPSYNC, expiredTime)
 
 
 			// act
 			// act
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 			syncGrafanaUserWithLdapUser(&ctx, &query)
 
 
 			// assert
 			// assert
-			So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
+			So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
 			So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
 		})
 		})
 	})
 	})
@@ -77,7 +79,7 @@ type mockSession struct {
 	value interface{}
 	value interface{}
 }
 }
 
 
-func (s *mockSession) Start(c *Context) error {
+func (s *mockSession) Start(c *macaron.Context) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -102,11 +104,11 @@ func (s *mockSession) Release() error {
 	return nil
 	return nil
 }
 }
 
 
-func (s *mockSession) Destory(c *Context) error {
+func (s *mockSession) Destory(c *macaron.Context) error {
 	return nil
 	return nil
 }
 }
 
 
-func (s *mockSession) RegenerateId(c *Context) error {
+func (s *mockSession) RegenerateId(c *macaron.Context) error {
 	return nil
 	return nil
 }
 }
 
 

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor