diff --git a/.circleci/config.yml b/.circleci/config.yml index f6d66daab84..209cf5c98cc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ version: 2 jobs: mysql-integration-test: docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.11.5 - image: circleci/mysql:5.6-ram environment: MYSQL_ROOT_PASSWORD: rootpass @@ -39,7 +39,7 @@ jobs: postgres-integration-test: docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.11.5 - image: circleci/postgres:9.3-ram environment: POSTGRES_USER: grafanatest @@ -74,7 +74,7 @@ jobs: gometalinter: docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.11.5 environment: # we need CGO because of go-sqlite3 CGO_ENABLED: 1 @@ -106,7 +106,7 @@ jobs: test-backend: docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.11.5 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -116,7 +116,7 @@ jobs: build-all: docker: - - image: grafana/build-container:1.2.2 + - image: grafana/build-container:1.2.3 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -147,9 +147,6 @@ jobs: - run: name: sha-sum packages command: 'go run build.go sha-dist' - - run: - name: Build Grafana.com master publisher - command: 'go build -o scripts/publish scripts/build/publish.go' - run: name: Test and build Grafana.com release publisher command: 'cd scripts/build/release_publisher && go test . && go build -o release_publisher .' @@ -158,13 +155,12 @@ jobs: paths: - dist/grafana* - scripts/*.sh - - scripts/publish - scripts/build/release_publisher/release_publisher - scripts/build/publish.sh build: docker: - - image: grafana/build-container:1.2.2 + - image: grafana/build-container:1.2.3 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -233,7 +229,7 @@ jobs: build-enterprise: docker: - - image: grafana/build-container:1.2.2 + - image: grafana/build-container:1.2.3 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -265,7 +261,7 @@ jobs: build-all-enterprise: docker: - - image: grafana/build-container:1.2.2 + - image: grafana/build-container:1.2.3 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -393,7 +389,8 @@ jobs: name: Publish to Grafana.com command: | rm dist/grafana-master-$(echo "${CIRCLE_SHA1}" | cut -b1-7).linux-x64.tar.gz - ./scripts/publish -apiKey ${GRAFANA_COM_API_KEY} + rm dist/*latest* + cd dist && ../scripts/build/release_publisher/release_publisher -apikey ${GRAFANA_COM_API_KEY} -from-local deploy-release: docker: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a691465626..be6141a4bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,34 +3,46 @@ ### New Features * **Alerting**: Adds support for Google Hangouts Chat notifications [#11221](https://github.com/grafana/grafana/issues/11221), thx [@PatrickSchuster](https://github.com/PatrickSchuster) * **Elasticsearch**: Support bucket script pipeline aggregations [#5968](https://github.com/grafana/grafana/issues/5968) +* **Influxdb**: Add support for time zone (`tz`) clause [#10322](https://github.com/grafana/grafana/issues/10322), thx [@cykl](https://github.com/cykl) * **Snapshots**: Enable deletion of public snapshot [#14109](https://github.com/grafana/grafana/issues/14109) +* **Provisioning**: Provisioning support for alert notifiers [#10487](https://github.com/grafana/grafana/issues/10487), thx [@pbakulev](https://github.com/pbakulev) ### Minor +* **Alerting**: Use seperate timeouts for alert evals and notifications [#14701](https://github.com/grafana/grafana/issues/14701), thx [@sharkpc0813](https://github.com/sharkpc0813) * **Elasticsearch**: Add support for offset in date histogram aggregation [#12653](https://github.com/grafana/grafana/issues/12653), thx [@mattiarossi](https://github.com/mattiarossi) * **Elasticsearch**: Add support for moving average and derivative using doc count (metric count) [#8843](https://github.com/grafana/grafana/issues/8843) [#11175](https://github.com/grafana/grafana/issues/11175) +* **Elasticsearch**: Add support for template variable interpolation in alias field [#4075](https://github.com/grafana/grafana/issues/4075), thx [@SamuelToh](https://github.com/SamuelToh) +* **Influxdb**: Fix autocomplete of measurements does not escape search string properly [#11503](https://github.com/grafana/grafana/issues/11503), thx [@SamuelToh](https://github.com/SamuelToh) +* **Stackdriver**: Aggregating series returns more than one series [#14581](https://github.com/grafana/grafana/issues/14581) and [#13914](https://github.com/grafana/grafana/issues/13914), thx [@kinok](https://github.com/kinok) +* **Cloudwatch**: Fix Assume Role Arn [#14722](https://github.com/grafana/grafana/issues/14722), thx [@jaken551](https://github.com/jaken551) +* **Postgres/MySQL/MSSQL**: Nanosecond timestamp support (`$__unixEpochNanoFilter`, `$__unixEpochNanoFrom`, `$__unixEpochNanoTo`) [#14711](https://github.com/grafana/grafana/pull/14711), thx [@ander26](https://github.com/ander26) +* **Provisioning**: Fixes bug causing infinite growth in dashboard_version table. [#12864](https://github.com/grafana/grafana/issues/12864) * **Auth**: Prevent password reset when login form is disabled or either LDAP or Auth Proxy is enabled [#14246](https://github.com/grafana/grafana/issues/14246), thx [@SilverFire](https://github.com/SilverFire) -* **Dataproxy**: Override incoming Authorization header [#13815](https://github.com/grafana/grafana/issues/13815), thx [@kornholi](https://github.com/kornholi) * **Admin**: Fix prevent removing last grafana admin permissions [#11067](https://github.com/grafana/grafana/issues/11067), thx [@danielbh](https://github.com/danielbh) -* **Templating**: Escaping "Custom" template variables [#13754](https://github.com/grafana/grafana/issues/13754), thx [@IntegersOfK](https://github.com/IntegersOfK) * **Admin**: When multiple user invitations, all links are the same as the first user who was invited [#14483](https://github.com/grafana/grafana/issues/14483) * **LDAP**: Upgrade go-ldap to v3 [#14548](https://github.com/grafana/grafana/issues/14548) -* **Proxy whitelist**: Add CIDR capability to auth_proxy whitelist [#14546](https://github.com/grafana/grafana/issues/14546), thx [@jacobrichard](https://github.com/jacobrichard) * **OAuth**: Support OAuth providers that are not RFC6749 compliant [#14562](https://github.com/grafana/grafana/issues/14562), thx [@tdabasinskas](https://github.com/tdabasinskas) +* **Proxy whitelist**: Add CIDR capability to auth_proxy whitelist [#14546](https://github.com/grafana/grafana/issues/14546), thx [@jacobrichard](https://github.com/jacobrichard) +* **Dashboard**: `Min width` changed to `Max per row` for repeating panels. This lets you specify the maximum number of panels to show per row and by that repeated panels will always take up full width of row [#12991](https://github.com/grafana/grafana/pull/12991), thx [@pgiraud](https://github.com/pgiraud) +* **Dashboard**: Retain decimal precision when exporting CSV [#13929](https://github.com/grafana/grafana/issues/13929), thx [@cinaglia](https://github.com/cinaglia) +* **Templating**: Escaping "Custom" template variables [#13754](https://github.com/grafana/grafana/issues/13754), thx [@IntegersOfK](https://github.com/IntegersOfK) * **Units**: Add blood glucose level units mg/dL and mmol/L [#14519](https://github.com/grafana/grafana/issues/14519), thx [@kjedamzik](https://github.com/kjedamzik) -* **Stackdriver**: Aggregating series returns more than one series [#14581](https://github.com/grafana/grafana/issues/14581) and [#13914](https://github.com/grafana/grafana/issues/13914), thx [@kinok](https://github.com/kinok) -* **Provisioning**: Fixes bug causing infinite growth in dashboard_version table. [#12864](https://github.com/grafana/grafana/issues/12864) +* **Units**: Add Floating Point Operations per Second units [#14558](https://github.com/grafana/grafana/pull/14558), thx [@hahnjo](https://github.com/hahnjo) +* **Table**: Renders epoch string as date if date column style [#14484](https://github.com/grafana/grafana/issues/14484) +* **Piechart/Flot**: Fixes multiple piechart instances with donut bug [#15062](https://github.com/grafana/grafana/pull/15062) +* **Dataproxy**: Override incoming Authorization header [#13815](https://github.com/grafana/grafana/issues/13815), thx [@kornholi](https://github.com/kornholi) +* **Dataproxy**: Add global datasource proxy timeout setting [#5699](https://github.com/grafana/grafana/issues/5699), thx [@RangerRick](https://github.com/RangerRick) +* **Database**: Support specifying database host using IPV6 for backend database and sql datasources [#13711](https://github.com/grafana/grafana/issues/13711), thx [@ellisvlad](https://github.com/ellisvlad) ### Bug fixes * **Search**: Fix for issue with scrolling the "tags filter" dropdown, fixes [#14486](https://github.com/grafana/grafana/issues/14486) * **Prometheus**: Query for annotation always uses 60s step regardless of dashboard range, fixes [#14795](https://github.com/grafana/grafana/issues/14795) +* **Annotations**: Fix creating annotation when graph panel has no data points position the popup outside viewport [#13765](https://github.com/grafana/grafana/issues/13765), thx [@banjeremy](https://github.com/banjeremy) ### Breaking changes -* **Text Panel**: The text panel does no longer by default allow unsantizied HTML. -* [#4117](https://github.com/grafana/grafana/issues/4117). This means that if you have text panels with scripts tags -* they will no longer work as before. To enable unsafe javascript execution in text panels enable the settings -* `disable_sanitize_html` under the section `[panels]` in your Grafana ini file, or set env variable -* `GF_PANELS_DISABLE_SANITIZE_HTML=true`. +* **Text Panel**: The text panel does no longer by default allow unsantizied HTML. [#4117](https://github.com/grafana/grafana/issues/4117). This means that if you have text panels with scripts tags they will no longer work as before. To enable unsafe javascript execution in text panels enable the settings `disable_sanitize_html` under the section `[panels]` in your Grafana ini file, or set env variable `GF_PANELS_DISABLE_SANITIZE_HTML=true`. +* **Dashboard**: Panel property `minSpan` replaced by `maxPerRow`. Dashboard migration will automatically migrate all dashboard panels using the `minSpan` property to the new `maxPerRow` property [#12991](https://github.com/grafana/grafana/pull/12991) # 5.4.3 (2019-01-14) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 94daa2dc4d5..62f610cab59 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards diff --git a/Dockerfile b/Dockerfile index c3af89b6092..c3e59c8048e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Golang build container -FROM golang:1.11.4 +FROM golang:1.11.5 WORKDIR $GOPATH/src/github.com/grafana/grafana @@ -19,11 +19,13 @@ COPY package.json package.json RUN go run build.go build # Node build container -FROM node:8 +FROM node:10.14.2 WORKDIR /usr/src/app/ COPY package.json yarn.lock ./ +COPY packages packages + RUN yarn install --pure-lockfile --no-progress COPY Gruntfile.js tsconfig.json tslint.json ./ diff --git a/PLUGIN_DEV.md b/PLUGIN_DEV.md index 168b21dbd88..47743e5fbf9 100644 --- a/PLUGIN_DEV.md +++ b/PLUGIN_DEV.md @@ -1,7 +1,7 @@ # Plugin Development -This document is not meant as complete guide for developing plugins but more as a changelog for changes in -Grafana that can impact plugin development. When ever you as plugin author encounter an issue with your plugin after +This document is not meant as a complete guide for developing plugins but more as a changelog for changes in +Grafana that can impact plugin development. Whenever you as a plugin author encounter an issue with your plugin after upgrading Grafana please check here before creating an issue. ## Links diff --git a/README.md b/README.md index ff5da04f209..3df6a383e05 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you have any problems please read the [troubleshooting guide](http://docs.gra Be sure to read the [getting started guide](http://docs.grafana.org/guides/gettingstarted/) and the other feature guides. ## Run from master -If you want to build a package yourself, or contribute - Here is a guide for how to do that. You can always find +If you want to build a package yourself, or contribute - here is a guide for how to do that. You can always find the latest master builds [here](https://grafana.com/grafana/download) ### Dependencies @@ -71,7 +71,7 @@ Open grafana in your browser (default: `http://localhost:3000`) and login with a ### Building a Docker image -There are two different ways to build a Grafana docker image. If you're machine is setup for Grafana development and you run linux/amd64 you can build just the image. Otherwise, there is the option to build Grafana completely within Docker. +There are two different ways to build a Grafana docker image. If your machine is setup for Grafana development and you run linux/amd64 you can build just the image. Otherwise, there is the option to build Grafana completely within Docker. Run the image you have built using: `docker run --rm -p 3000:3000 grafana/grafana:dev` @@ -90,7 +90,7 @@ Choose this option to build on platforms other than linux/amd64 and/or not have The resulting image will be tagged as `grafana/grafana:dev` -Notice: If you are using Docker for MacOS, be sure to let limit of Memory bigger than 2 GiB (at docker -> Preferences -> Advanced), otherwize you may faild at `grunt build` +Notice: If you are using Docker for MacOS, be sure to set the memory limit to be larger than 2 GiB (at docker -> Preferences -> Advanced), otherwise `grunt build` may fail. ### Dev config @@ -129,8 +129,8 @@ GRAFANA_TEST_DB=postgres go test ./pkg/... ## Contribute -If you have any idea for an improvement or found a bug, do not hesitate to open an issue. -And if you have time clone this repo and submit a pull request and help me make Grafana +If you have any ideas for improvement or have found a bug, do not hesitate to open an issue. +And if you have time, clone this repo and submit a pull request to help me make Grafana the kickass metrics & devops dashboard we all dream about! Read the [contributing](https://github.com/grafana/grafana/blob/master/CONTRIBUTING.md) guide then check the [`beginner friendly`](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) label to find issues that are easy and that we would like help with. diff --git a/appveyor.yml b/appveyor.yml index 5f97784dd38..ccf9b5a06e1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ clone_folder: c:\gopath\src\github.com\grafana\grafana environment: nodejs_version: "8" GOPATH: C:\gopath - GOVERSION: 1.11.4 + GOVERSION: 1.11.5 install: - rmdir c:\go /s /q diff --git a/conf/defaults.ini b/conf/defaults.ini index 37e1ee2c7df..788112ae67e 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -106,6 +106,22 @@ path = grafana.db # For "sqlite3" only. cache mode setting used for connecting to the database cache_mode = private +#################################### Login ############################### + +[login] + +# Login cookie name +cookie_name = grafana_session + +# How many days an session can be unused before we inactivate it +login_remember_days = 7 + +# How often should the login token be rotated. default to '10m' +rotate_token_minutes = 10 + +# How long should Grafana keep expired tokens before deleting them +delete_expired_token_after_days = 30 + #################################### Session ############################# [session] # Either "memory", "file", "redis", "mysql", "postgres", "memcache", default is "file" @@ -143,6 +159,9 @@ conn_max_lifetime = 14400 # This enables data proxy logging, default is false logging = false +# How long the data proxy should wait before timing out default is 30 (seconds) +timeout = 30 + #################################### Analytics ########################### [analytics] # Server reporting, sends usage counters to stats.grafana.org every 24 hours. @@ -175,11 +194,6 @@ admin_password = admin # used for signing secret_key = SW2YcwTIb9zpOOhoPsMm -# Auto-login remember days -login_remember_days = 7 -cookie_username = grafana_user -cookie_remember_name = grafana_remember - # disable gravatar profile images disable_gravatar = false @@ -189,6 +203,9 @@ data_source_proxy_whitelist = # disable protection against brute force login attempts disable_brute_force_login_protection = false +# set cookies as https only. default is false +https_flag_cookies = false + #################################### Snapshots ########################### [snapshots] # snapshot sharing options @@ -490,7 +507,7 @@ concurrent_render_limit = 5 #################################### Explore ############################# [explore] # Enable the Explore section -enabled = false +enabled = true #################################### Internal Grafana Metrics ############ # Metrics available at HTTP API Url /metrics diff --git a/conf/provisioning/notifiers/sample.yaml b/conf/provisioning/notifiers/sample.yaml new file mode 100644 index 00000000000..7d909839412 --- /dev/null +++ b/conf/provisioning/notifiers/sample.yaml @@ -0,0 +1,25 @@ +# # config file version +apiVersion: 1 + +# notifiers: +# - name: default-slack-temp +# type: slack +# org_name: Main Org. +# is_default: true +# uid: notifier1 +# settings: +# recipient: "XXX" +# token: "xoxb" +# uploadImage: true +# url: https://slack.com +# - name: default-email +# type: email +# org_id: 1 +# uid: notifier2 +# is_default: false +# settings: +# addresses: example11111@example.com +# delete_notifiers: +# - name: default-slack-temp +# org_name: Main Org. +# uid: notifier1 \ No newline at end of file diff --git a/conf/sample.ini b/conf/sample.ini index 96b92db6f48..89880106345 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -102,6 +102,22 @@ log_queries = # For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) ;cache_mode = private +#################################### Login ############################### + +[login] + +# Login cookie name +;cookie_name = grafana_session + +# How many days an session can be unused before we inactivate it +;login_remember_days = 7 + +# How often should the login token be rotated. default to '10' +;rotate_token_minutes = 10 + +# How long should Grafana keep expired tokens before deleting them +;delete_expired_token_after_days = 30 + #################################### Session #################################### [session] # Either "memory", "file", "redis", "mysql", "postgres", default is "file" @@ -130,6 +146,9 @@ log_queries = # This enables data proxy logging, default is false ;logging = false +# How long the data proxy should wait before timing out default is 30 (seconds) +;timeout = 30 + #################################### Analytics #################################### [analytics] # Server reporting, sends usage counters to stats.grafana.org every 24 hours. @@ -162,11 +181,6 @@ log_queries = # used for signing ;secret_key = SW2YcwTIb9zpOOhoPsMm -# Auto-login remember days -;login_remember_days = 7 -;cookie_username = grafana_user -;cookie_remember_name = grafana_remember - # disable gravatar profile images ;disable_gravatar = false @@ -176,6 +190,9 @@ log_queries = # disable protection against brute force login attempts ;disable_brute_force_login_protection = false +# set cookies as https only. default is false +;https_flag_cookies = false + #################################### Snapshots ########################### [snapshots] # snapshot sharing options @@ -415,7 +432,7 @@ log_queries = #################################### Explore ############################# [explore] # Enable the Explore section -;enabled = false +;enabled = true #################################### Internal Grafana Metrics ########################## # Metrics available at HTTP API Url /metrics diff --git a/devenv/docker/ha_test/docker-compose.yaml b/devenv/docker/ha_test/docker-compose.yaml index 1195e2a977c..504ee86404d 100644 --- a/devenv/docker/ha_test/docker-compose.yaml +++ b/devenv/docker/ha_test/docker-compose.yaml @@ -54,7 +54,8 @@ services: # - GF_DATABASE_SSL_MODE=disable # - GF_SESSION_PROVIDER=postgres # - GF_SESSION_PROVIDER_CONFIG=user=grafana password=password host=db port=5432 dbname=grafana sslmode=disable - - GF_LOG_FILTERS=alerting.notifier:debug,alerting.notifier.slack:debug + - GF_LOG_FILTERS=alerting.notifier:debug,alerting.notifier.slack:debug,auth:debug + - GF_LOGIN_ROTATE_TOKEN_MINUTES=2 ports: - 3000 depends_on: diff --git a/devenv/docker/loadtest/README.md b/devenv/docker/loadtest/README.md new file mode 100644 index 00000000000..8e724637acb --- /dev/null +++ b/devenv/docker/loadtest/README.md @@ -0,0 +1,69 @@ +# Grafana load test + +Runs load tests and checks using [k6](https://k6.io/). + +## Prerequisites + +Docker + +## Run + +Run load test for 15 minutes: + +```bash +$ ./run.sh +``` + +Run load test for custom duration: + +```bash +$ ./run.sh -d 10s +``` + +Example output: + +```bash + + /\ |‾‾| /‾‾/ /‾/ + /\ / \ | |_/ / / / + / \/ \ | | / ‾‾\ + / \ | |‾\ \ | (_) | + / __________ \ |__| \__\ \___/ .io + + execution: local + output: - + script: src/auth_token_test.js + + duration: 15m0s, iterations: - + vus: 2, max: 2 + + done [==========================================================] 15m0s / 15m0s + + █ user auth token test + + █ user authenticates thru ui with username and password + + ✓ response status is 200 + ✓ response has cookie 'grafana_session' with 32 characters + + █ batch tsdb requests + + ✓ response status is 200 + + checks.....................: 100.00% ✓ 32844 ✗ 0 + data_received..............: 411 MB 457 kB/s + data_sent..................: 12 MB 14 kB/s + group_duration.............: avg=95.64ms min=16.42ms med=94.35ms max=307.52ms p(90)=137.78ms p(95)=146.75ms + http_req_blocked...........: avg=1.27ms min=942ns med=610.08µs max=48.32ms p(90)=2.92ms p(95)=4.25ms + http_req_connecting........: avg=1.06ms min=0s med=456.79µs max=47.19ms p(90)=2.55ms p(95)=3.78ms + http_req_duration..........: avg=58.16ms min=1ms med=52.59ms max=293.35ms p(90)=109.53ms p(95)=120.19ms + http_req_receiving.........: avg=38.98µs min=6.43µs med=32.55µs max=16.2ms p(90)=64.63µs p(95)=78.8µs + http_req_sending...........: avg=328.66µs min=8.09µs med=110.77µs max=44.13ms p(90)=552.65µs p(95)=1.09ms + http_req_tls_handshaking...: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s + http_req_waiting...........: avg=57.79ms min=935.02µs med=52.15ms max=293.06ms p(90)=109.04ms p(95)=119.71ms + http_reqs..................: 34486 38.317775/s + iteration_duration.........: avg=1.09s min=1.81µs med=1.09s max=1.3s p(90)=1.13s p(95)=1.14s + iterations.................: 1642 1.824444/s + vus........................: 2 min=2 max=2 + vus_max....................: 2 min=2 max=2 +``` diff --git a/devenv/docker/loadtest/auth_token_test.js b/devenv/docker/loadtest/auth_token_test.js new file mode 100644 index 00000000000..e1356fb6f9a --- /dev/null +++ b/devenv/docker/loadtest/auth_token_test.js @@ -0,0 +1,71 @@ +import { sleep, check, group } from 'k6'; +import { createClient, createBasicAuthClient } from './modules/client.js'; +import { createTestOrgIfNotExists, createTestdataDatasourceIfNotExists } from './modules/util.js'; + +export let options = { + noCookiesReset: true +}; + +let endpoint = __ENV.URL || 'http://localhost:3000'; +const client = createClient(endpoint); + +export const setup = () => { + const basicAuthClient = createBasicAuthClient(endpoint, 'admin', 'admin'); + const orgId = createTestOrgIfNotExists(basicAuthClient); + const datasourceId = createTestdataDatasourceIfNotExists(basicAuthClient); + client.withOrgId(orgId); + return { + orgId: orgId, + datasourceId: datasourceId, + }; +} + +export default (data) => { + group("user auth token test", () => { + if (__ITER === 0) { + group("user authenticates thru ui with username and password", () => { + let res = client.ui.login('admin', 'admin'); + + check(res, { + 'response status is 200': (r) => r.status === 200, + 'response has cookie \'grafana_session\' with 32 characters': (r) => r.cookies.grafana_session[0].value.length === 32, + }); + }); + } + + if (__ITER !== 0) { + group("batch tsdb requests", () => { + const batchCount = 20; + const requests = []; + const payload = { + from: '1547765247624', + to: '1547768847624', + queries: [{ + refId: 'A', + scenarioId: 'random_walk', + intervalMs: 10000, + maxDataPoints: 433, + datasourceId: data.datasourceId, + }] + }; + + requests.push({ method: 'GET', url: '/api/annotations?dashboardId=2074&from=1548078832772&to=1548082432772' }); + + for (let n = 0; n < batchCount; n++) { + requests.push({ method: 'POST', url: '/api/tsdb/query', body: payload }); + } + + let responses = client.batch(requests); + for (let n = 0; n < batchCount; n++) { + check(responses[n], { + 'response status is 200': (r) => r.status === 200, + }); + } + }); + } + }); + + sleep(1) +} + +export const teardown = (data) => {} diff --git a/devenv/docker/loadtest/modules/client.js b/devenv/docker/loadtest/modules/client.js new file mode 100644 index 00000000000..bda0da64564 --- /dev/null +++ b/devenv/docker/loadtest/modules/client.js @@ -0,0 +1,187 @@ +import http from "k6/http"; +import encoding from 'k6/encoding'; + +export const UIEndpoint = class UIEndpoint { + constructor(httpClient) { + this.httpClient = httpClient; + } + + login(username, pwd) { + const payload = { user: username, password: pwd }; + return this.httpClient.formPost('/login', payload); + } +} + +export const DatasourcesEndpoint = class DatasourcesEndpoint { + constructor(httpClient) { + this.httpClient = httpClient; + } + + getById(id) { + return this.httpClient.get(`/datasources/${id}`); + } + + getByName(name) { + return this.httpClient.get(`/datasources/name/${name}`); + } + + create(payload) { + return this.httpClient.post(`/datasources`, JSON.stringify(payload)); + } + + delete(id) { + return this.httpClient.delete(`/datasources/${id}`); + } +} + +export const OrganizationsEndpoint = class OrganizationsEndpoint { + constructor(httpClient) { + this.httpClient = httpClient; + } + + getById(id) { + return this.httpClient.get(`/orgs/${id}`); + } + + getByName(name) { + return this.httpClient.get(`/orgs/name/${name}`); + } + + create(name) { + let payload = { + name: name, + }; + return this.httpClient.post(`/orgs`, JSON.stringify(payload)); + } + + delete(id) { + return this.httpClient.delete(`/orgs/${id}`); + } +} + +export const GrafanaClient = class GrafanaClient { + constructor(httpClient) { + httpClient.onBeforeRequest = this.onBeforeRequest; + this.raw = httpClient; + this.ui = new UIEndpoint(httpClient); + this.orgs = new OrganizationsEndpoint(httpClient.withUrl('/api')); + this.datasources = new DatasourcesEndpoint(httpClient.withUrl('/api')); + } + + batch(requests) { + return this.raw.batch(requests); + } + + withOrgId(orgId) { + this.orgId = orgId; + } + + onBeforeRequest(params) { + if (this.orgId && this.orgId > 0) { + params = params.headers || {}; + params.headers["X-Grafana-Org-Id"] = this.orgId; + } + } +} + +export const BaseClient = class BaseClient { + constructor(url, subUrl) { + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); + } + + if (subUrl.endsWith('/')) { + subUrl = subUrl.substring(0, subUrl.length - 1); + } + + this.url = url + subUrl; + this.onBeforeRequest = () => {}; + } + + withUrl(subUrl) { + let c = new BaseClient(this.url, subUrl); + c.onBeforeRequest = this.onBeforeRequest; + return c; + } + + beforeRequest(params) { + + } + + get(url, params) { + params = params || {}; + this.beforeRequest(params); + this.onBeforeRequest(params); + return http.get(this.url + url, params); + } + + formPost(url, body, params) { + params = params || {}; + this.beforeRequest(params); + this.onBeforeRequest(params); + return http.post(this.url + url, body, params); + } + + post(url, body, params) { + params = params || {}; + params.headers = params.headers || {}; + params.headers['Content-Type'] = 'application/json'; + + this.beforeRequest(params); + this.onBeforeRequest(params); + return http.post(this.url + url, body, params); + } + + delete(url, params) { + params = params || {}; + this.beforeRequest(params); + this.onBeforeRequest(params); + return http.del(this.url + url, null, params); + } + + batch(requests) { + for (let n = 0; n < requests.length; n++) { + let params = requests[n].params || {}; + params.headers = params.headers || {}; + params.headers['Content-Type'] = 'application/json'; + this.beforeRequest(params); + this.onBeforeRequest(params); + requests[n].params = params; + requests[n].url = this.url + requests[n].url; + if (requests[n].body) { + requests[n].body = JSON.stringify(requests[n].body); + } + } + + return http.batch(requests); + } +} + +export class BasicAuthClient extends BaseClient { + constructor(url, subUrl, username, password) { + super(url, subUrl); + this.username = username; + this.password = password; + } + + withUrl(subUrl) { + let c = new BasicAuthClient(this.url, subUrl, this.username, this.password); + c.onBeforeRequest = this.onBeforeRequest; + return c; + } + + beforeRequest(params) { + params = params || {}; + params.headers = params.headers || {}; + let token = `${this.username}:${this.password}`; + params.headers['Authorization'] = `Basic ${encoding.b64encode(token)}`; + } +} + +export const createClient = (url) => { + return new GrafanaClient(new BaseClient(url, '')); +} + +export const createBasicAuthClient = (url, username, password) => { + return new GrafanaClient(new BasicAuthClient(url, '', username, password)); +} diff --git a/devenv/docker/loadtest/modules/util.js b/devenv/docker/loadtest/modules/util.js new file mode 100644 index 00000000000..af6d4cdac09 --- /dev/null +++ b/devenv/docker/loadtest/modules/util.js @@ -0,0 +1,35 @@ +export const createTestOrgIfNotExists = (client) => { + let orgId = 0; + let res = client.orgs.getByName('k6'); + if (res.status === 404) { + res = client.orgs.create('k6'); + if (res.status !== 200) { + throw new Error('Expected 200 response status when creating org'); + } + orgId = res.json().orgId; + } else { + orgId = res.json().id; + } + + client.withOrgId(orgId); + return orgId; +} + +export const createTestdataDatasourceIfNotExists = (client) => { + const payload = { + access: 'proxy', + isDefault: false, + name: 'k6-testdata', + type: 'testdata', + }; + + let res = client.datasources.getByName(payload.name); + if (res.status === 404) { + res = client.datasources.create(payload); + if (res.status !== 200) { + throw new Error('Expected 200 response status when creating datasource'); + } + } + + return res.json().id; +} diff --git a/devenv/docker/loadtest/run.sh b/devenv/docker/loadtest/run.sh new file mode 100755 index 00000000000..474d75383b6 --- /dev/null +++ b/devenv/docker/loadtest/run.sh @@ -0,0 +1,24 @@ +#/bin/bash + +PWD=$(pwd) + +run() { + duration='15m' + url='http://localhost:3000' + + while getopts ":d:u:" o; do + case "${o}" in + d) + duration=${OPTARG} + ;; + u) + url=${OPTARG} + ;; + esac + done + shift $((OPTIND-1)) + + docker run -t --network=host -v $PWD:/src -e URL=$url --rm -i loadimpact/k6:master run --vus 2 --duration $duration src/auth_token_test.js +} + +run "$@" diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index b2a1b1f42e7..cce25e4cf2b 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -231,3 +231,187 @@ By default Grafana will delete dashboards in the database if the file is removed > which leads to problems if you re-use settings that are supposed to be unique. > Be careful not to re-use the same `title` multiple times within a folder > or `uid` within the same installation as this will cause weird behaviors. + +## Alert Notification Channels + +Alert Notification Channels can be provisioned by adding one or more yaml config files in the [`provisioning/notifiers`](/installation/configuration/#provisioning) directory. + +Each config file can contain the following top-level fields: +- `notifiers`, a list of alert notifications that will be added or updated during start up. If the notification channel already exists, Grafana will update it to match the configuration file. +- `delete_notifiers`, a list of alert notifications to be deleted before before inserting/updating those in the `notifiers` list. + +Provisioning looks up alert notifications by uid, and will update any existing notification with the provided uid. + +By default, exporting a dashboard as JSON will use a sequential identifier to refer to alert notifications. The field `uid` can be optionally specified to specify a string identifier for the alert name. + +```json +{ + ... + "alert": { + ..., + "conditions": [...], + "frequency": "24h", + "noDataState": "ok", + "notifications": [ + {"uid": "notifier1"}, + {"uid": "notifier2"}, + ] + } + ... +} +``` + +### Example Alert Notification Channels Config File + +```yaml +notifiers: + - name: notification-channel-1 + type: slack + uid: notifier1 + # either + org_id: 2 + # or + org_name: Main Org. + is_default: true + # See `Supported Settings` section for settings supporter for each + # alert notification type. + settings: + recipient: "XXX" + token: "xoxb" + uploadImage: true + url: https://slack.com + +delete_notifiers: + - name: notification-channel-1 + uid: notifier1 + # either + org_id: 2 + # or + org_name: Main Org. + - name: notification-channel-2 + # default org_id: 1 +``` + +### Supported Settings + +The following sections detail the supported settings for each alert notification type. + +#### Alert notification `pushover` + +| Name | +| ---- | +| apiToken | +| userKey | +| device | +| retry | +| expire | + +#### Alert notification `slack` + +| Name | +| ---- | +| url | +| recipient | +| username | +| iconEmoji | +| iconUrl | +| uploadImage | +| mention | +| token | + +#### Alert notification `victorops` + +| Name | +| ---- | +| url | + +#### Alert notification `kafka` + +| Name | +| ---- | +| kafkaRestProxy | +| kafkaTopic | + +#### Alert notification `LINE` + +| Name | +| ---- | +| token | + +#### Alert notification `pagerduty` + +| Name | +| ---- | +| integrationKey | + +#### Alert notification `sensu` + +| Name | +| ---- | +| url | +| source | +| handler | +| username | +| password | + +#### Alert notification `prometheus-alertmanager` + +| Name | +| ---- | +| url | + +#### Alert notification `teams` + +| Name | +| ---- | +| url | + +#### Alert notification `dingding` + +| Name | +| ---- | +| url | + +#### Alert notification `email` + +| Name | +| ---- | +| addresses | + +#### Alert notification `hipchat` + +| Name | +| ---- | +| url | +| apikey | +| roomid | + +#### Alert notification `opsgenie` + +| Name | +| ---- | +| apiKey | +| apiUrl | + +#### Alert notification `telegram` + +| Name | +| ---- | +| bottoken | +| chatid | + +#### Alert notification `threema` + +| Name | +| ---- | +| gateway_id | +| recipient_id | +| api_secret | + +#### Alert notification `webhook` + +| Name | +| ---- | +| url | +| username | +| password | \ No newline at end of file diff --git a/docs/sources/features/datasources/mssql.md b/docs/sources/features/datasources/mssql.md index cd191f14273..5bab14c05fa 100644 --- a/docs/sources/features/datasources/mssql.md +++ b/docs/sources/features/datasources/mssql.md @@ -110,6 +110,9 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochNanoFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as nanosecond timestamp. For example, *dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872* +*$__unixEpochNanoFrom()* | Will be replaced by the start of the currently active time selection as nanosecond timestamp. For example, *1494410783152415214* +*$__unixEpochNanoTo()* | Will be replaced by the end of the currently active time selection as nanosecond timestamp. For example, *1494497183142514872* *$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup but for times stored as unix timestamp (only available in Grafana 5.3+). *$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above but also adds a column alias (only available in Grafana 5.3+). diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 371c92cde70..bafe79bd09a 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -144,6 +144,9 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochNanoFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as nanosecond timestamp. For example, *dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872* +*$__unixEpochNanoFrom()* | Will be replaced by the start of the currently active time selection as nanosecond timestamp. For example, *1494410783152415214* +*$__unixEpochNanoTo()* | Will be replaced by the end of the currently active time selection as nanosecond timestamp. For example, *1494497183142514872* *$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup but for times stored as unix timestamp (only available in Grafana 5.3+). *$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above but also adds a column alias (only available in Grafana 5.3+). diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 52f8804f27f..df6bf555077 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -154,6 +154,9 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamps. For example, *dateColumn >= 1494410783 AND dateColumn <= 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochNanoFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as nanosecond timestamps. For example, *dateColumn >= 1494410783152415214 AND dateColumn <= 1494497183142514872* +*$__unixEpochNanoFrom()* | Will be replaced by the start of the currently active time selection as nanosecond timestamp. For example, *1494410783152415214* +*$__unixEpochNanoTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183142514872* *$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup, but for times stored as unix timestamp (only available in Grafana 5.3+). *$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above, but also adds a column alias (only available in Grafana 5.3+). diff --git a/docs/sources/http_api/data_source.md b/docs/sources/http_api/data_source.md index 9aaf29ec5f4..364b55b0cfc 100644 --- a/docs/sources/http_api/data_source.md +++ b/docs/sources/http_api/data_source.md @@ -188,8 +188,8 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk "defaultRegion": "us-west-1" }, "secureJsonData": { - "accessKey": "Ol4pIDpeKSA6XikgOl4p", //should not be encoded - "secretKey": "dGVzdCBrZXkgYmxlYXNlIGRvbid0IHN0ZWFs" //should be Base-64 encoded + "accessKey": "Ol4pIDpeKSA6XikgOl4p", + "secretKey": "dGVzdCBrZXkgYmxlYXNlIGRvbid0IHN0ZWFs" } } ``` diff --git a/docs/sources/http_api/other.md b/docs/sources/http_api/other.md index 5bf0cde05fe..ea905bf88f0 100644 --- a/docs/sources/http_api/other.md +++ b/docs/sources/http_api/other.md @@ -82,4 +82,29 @@ HTTP/1.1 200 Content-Type: application/json {"message": "Logged in"} -``` \ No newline at end of file +``` + +# Health API + +## Returns health information about Grafana + +`GET /api/health` + +**Example Request** + +```http +GET /api/health +Accept: application/json +``` + +**Example Response**: + +```http +HTTP/1.1 200 OK + +{ + "commit": "087143285", + "database": "ok", + "version": "5.1.3" +} +``` diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 8fa51c88554..46bab83654e 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -391,6 +391,12 @@ value is `true`. If you want to track Grafana usage via Google analytics specify *your* Universal Analytics ID here. By default this feature is disabled. +### check_for_updates + +Set to false to disable all checks to https://grafana.com for new versions of Grafana and installed plugins. Check is used +in some UI views to notify that a Grafana or plugin update exists. This option does not cause any auto updates, nor +send any sensitive information. +