Merge branch 'master' into react-panels

This commit is contained in:
Torkel Ödegaard 2018-08-24 17:29:10 +02:00
commit 0b794ff685
465 changed files with 20245 additions and 11319 deletions

View File

@ -9,7 +9,7 @@ watch_dirs = [
"$WORKDIR/public/views",
"$WORKDIR/conf",
]
watch_exts = [".go", ".ini", ".toml"]
watch_exts = [".go", ".ini", ".toml", ".template.html"]
build_delay = 1500
cmds = [
["go", "run", "build.go", "-dev", "build-server"],

View File

@ -5,9 +5,11 @@ aliases:
ignore: /.*/
tags:
only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
- &filter-not-release
- &filter-not-release-or-master
tags:
ignore: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
branches:
ignore: master
- &filter-only-master
branches:
only: master
@ -89,7 +91,7 @@ jobs:
name: run linters
command: 'gometalinter.v2 --enable-gc --vendor --deadline 10m --disable-all --enable=deadcode --enable=ineffassign --enable=structcheck --enable=unconvert --enable=varcheck ./...'
- run:
name: run go vet
name: run go vet
command: 'go vet ./pkg/...'
test-frontend:
@ -102,6 +104,7 @@ jobs:
- run:
name: yarn install
command: 'yarn install --pure-lockfile --no-progress'
no_output_timeout: 15m
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
@ -144,6 +147,12 @@ jobs:
- run:
name: sign packages
command: './scripts/build/sign_packages.sh'
- run:
name: verify signed packages
command: |
mkdir -p ~/.rpmdb/pubkeys
curl -s https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana > ~/.rpmdb/pubkeys/grafana.key
./scripts/build/verify_signed_packages.sh dist/*.rpm
- run:
name: sha-sum packages
command: 'go run build.go sha-dist'
@ -156,8 +165,65 @@ jobs:
- dist/grafana*
- scripts/*.sh
- scripts/publish
- store_artifacts:
path: dist
build:
docker:
- image: grafana/build-container:1.0.0
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
- run:
name: prepare build tools
command: '/tmp/bootstrap.sh'
- 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'
- persist_to_workspace:
root: .
paths:
- dist/grafana*
grafana-docker-master:
docker:
- image: docker:stable-git
steps:
- checkout
- attach_workspace:
at: .
- setup_remote_docker
- run: docker info
- run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker
- run: cd packaging/docker && ./build-deploy.sh "master-${CIRCLE_SHA1}"
grafana-docker-pr:
docker:
- image: docker:stable-git
steps:
- checkout
- attach_workspace:
at: .
- setup_remote_docker
- run: docker info
- run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker
- run: cd packaging/docker && ./build.sh "${CIRCLE_SHA1}"
grafana-docker-release:
docker:
- image: docker:stable-git
steps:
- checkout
- attach_workspace:
at: .
- setup_remote_docker
- run: docker info
- run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker
- run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}"
build-enterprise:
docker:
@ -213,9 +279,6 @@ jobs:
- 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} master-$(echo "${CIRCLE_SHA1}" | cut -b1-7)'
- run:
name: Publish to Grafana.com
command: |
@ -237,30 +300,27 @@ jobs:
- 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:
build-master:
jobs:
- build-all:
filters: *filter-only-master
- build-enterprise:
filters: *filter-only-master
- codespell:
filters: *filter-not-release
filters: *filter-only-master
- gometalinter:
filters: *filter-not-release
filters: *filter-only-master
- test-frontend:
filters: *filter-not-release
filters: *filter-only-master
- test-backend:
filters: *filter-not-release
filters: *filter-only-master
- mysql-integration-test:
filters: *filter-not-release
filters: *filter-only-master
- postgres-integration-test:
filters: *filter-not-release
filters: *filter-only-master
- deploy-master:
requires:
- build-all
@ -270,7 +330,17 @@ workflows:
- gometalinter
- mysql-integration-test
- postgres-integration-test
filters: *filter-only-master
filters: *filter-only-master
- grafana-docker-master:
requires:
- build-all
- test-backend
- test-frontend
- codespell
- gometalinter
- mysql-integration-test
- postgres-integration-test
filters: *filter-only-master
- deploy-enterprise-master:
requires:
- build-all
@ -309,3 +379,40 @@ workflows:
- mysql-integration-test
- postgres-integration-test
filters: *filter-only-release
- grafana-docker-release:
requires:
- build-all
- test-backend
- test-frontend
- codespell
- gometalinter
- mysql-integration-test
- postgres-integration-test
filters: *filter-only-release
build-branches-and-prs:
jobs:
- build:
filters: *filter-not-release-or-master
- codespell:
filters: *filter-not-release-or-master
- gometalinter:
filters: *filter-not-release-or-master
- test-frontend:
filters: *filter-not-release-or-master
- test-backend:
filters: *filter-not-release-or-master
- mysql-integration-test:
filters: *filter-not-release-or-master
- postgres-integration-test:
filters: *filter-not-release-or-master
- grafana-docker-pr:
requires:
- build
- test-backend
- test-frontend
- codespell
- gometalinter
- mysql-integration-test
- postgres-integration-test
filters: *filter-not-release-or-master

View File

@ -3,9 +3,12 @@
.git
.gitignore
.github
.vscode
bin
data*
dist
docker
Dockerfile
docs
dump.rdb
node_modules

View File

@ -2,12 +2,12 @@ Follow the setup guide in README.md
### Rebuild frontend assets on source change
```
grunt && grunt watch
yarn watch
```
### Rerun tests on source change
```
grunt karma:dev
yarn jest
```
### Run tests for backend assets before commit
@ -17,6 +17,6 @@ test -z "$(gofmt -s -l . | grep -v -E 'vendor/(github.com|golang.org|gopkg.in)'
### Run tests for frontend assets before commit
```
npm test
yarn test
go test -v ./pkg/...
```

3
.gitignore vendored
View File

@ -58,6 +58,7 @@ debug.test
/examples/*/dist
/packaging/**/*.rpm
/packaging/**/*.deb
/packaging/**/*.tar.gz
# Ignore OSX indexing
.DS_Store
@ -70,4 +71,4 @@ debug.test
/vendor/**/appengine*
*.orig
/devenv/dashboards/bulk-testing/*.json
/devenv/bulk-dashboards/*.json

View File

@ -1,13 +0,0 @@
{
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}

View File

@ -1,37 +0,0 @@
{
"browser": true,
"esversion": 6,
"bitwise":false,
"curly": true,
"eqnull": true,
"strict": false,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"sub": true,
"unused": true,
"maxdepth": 6,
"maxlen": 140,
"globals": {
"System": true,
"Promise": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true
}
}

View File

@ -1,28 +1,87 @@
# 5.3.0 (unreleased)
* **OAuth**: Gitlab OAuth with support for filter by groups [#5623](https://github.com/grafana/grafana/issues/5623), thx [@BenoitKnecht](https://github.com/BenoitKnecht)
* **Dataproxy**: Pass configured/auth headers to a Datasource [#10971](https://github.com/grafana/grafana/issues/10971), thx [@mrsiano](https://github.com/mrsiano)
* **Cleanup**: Make temp file time to live configurable [#11607](https://github.com/grafana/grafana/issues/11607), thx [@xapon](https://github.com/xapon)
* **LDAP**: Define Grafana Admin permission in ldap group mappings [#2469](https://github.com/grafana/grafana/issues/2496), PR [#12622](https://github.com/grafana/grafana/issues/12622)
* **Cloudwatch**: CloudWatch GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
* **Configuration**: Allow auto-assigning users to specific organization (other than Main. Org) [#1823](https://github.com/grafana/grafana/issues/1823) [#12801](https://github.com/grafana/grafana/issues/12801), thx [@gzzo](https://github.com/gzzo) and [@ofosos](https://github.com/ofosos)
* **Profile**: List teams that the user is member of in current/active organization [#12476](https://github.com/grafana/grafana/issues/12476)
* **LDAP**: Client certificates support [#12805](https://github.com/grafana/grafana/issues/12805), thx [@nyxi](https://github.com/nyxi)
* **Postgres**: TimescaleDB support, e.g. use `time_bucket` for grouping by time when option enabled [#12680](https://github.com/grafana/grafana/pull/12680), thx [svenklemm](https://github.com/svenklemm)
### Minor
* **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
* **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248)
* **Dashboard**: Use uid when linking to dashboards internally in a dashboard [#10705](https://github.com/grafana/grafana/issues/10705)
* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps)
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
* **Prometheus**: Heatmap - fix unhandled error when some points are missing [#12484](https://github.com/grafana/grafana/issues/12484)
* **Prometheus**: Add $__interval, $__interval_ms, $__range, $__range_s & $__range_ms support for dashboard and template queries [#12597](https://github.com/grafana/grafana/issues/12597) [#12882](https://github.com/grafana/grafana/issues/12882), thx [@roidelapluie](https://github.com/roidelapluie)
* **Variables**: Skip unneeded extra query request when de-selecting variable values used for repeated panels [#8186](https://github.com/grafana/grafana/issues/8186), thx [@mtanda](https://github.com/mtanda)
* **Variables**: Limit amount of queries executed when updating variable that other variable(s) are dependent on [#11890](https://github.com/grafana/grafana/issues/11890)
* **Variables**: Support query variable refresh when another variable referenced in `Regex` field change its value [#12952](https://github.com/grafana/grafana/issues/12952), thx [@franciscocpg](https://github.com/franciscocpg)
* **Variables**: Support variables in query variable `Custom all value` field [#12965](https://github.com/grafana/grafana/issues/12965), thx [@franciscocpg](https://github.com/franciscocpg)
* **Postgres/MySQL/MSSQL**: New $__unixEpochGroup and $__unixEpochGroupAlias macros [#12892](https://github.com/grafana/grafana/issues/12892), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Add previous fill mode to $__timeGroup macro which will fill in previously seen value when point is missing [#12756](https://github.com/grafana/grafana/issues/12756), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Use floor rounding in $__timeGroup macro function [#12460](https://github.com/grafana/grafana/issues/12460), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Use metric column as prefix when returning multiple value columns [#12727](https://github.com/grafana/grafana/issues/12727), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: New $__timeGroupAlias macro. Postgres $__timeGroup no longer automatically adds time column alias [#12749](https://github.com/grafana/grafana/issues/12749), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Escape single quotes in variables [#12785](https://github.com/grafana/grafana/issues/12785), thx [@eMerzh](https://github.com/eMerzh)
* **MySQL/MSSQL**: Use datetime format instead of epoch for $__timeFilter, $__timeFrom and $__timeTo macros [#11618](https://github.com/grafana/grafana/issues/11618) [#11619](https://github.com/grafana/grafana/issues/11619), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
* **Postgres**: Escape ssl mode parameter in connectionstring [#12644](https://github.com/grafana/grafana/issues/12644), thx [@yogyrahmawan](https://github.com/yogyrahmawan)
* **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber)
* **Alerting**: Fix diff and percent_diff reducers [#11563](https://github.com/grafana/grafana/issues/11563), thx [@jessetane](https://github.com/jessetane)
* **Alerting**: Fix rendering timeout which could cause notifications to not be sent due to rendering timing out [#12151](https://github.com/grafana/grafana/issues/12151)
* **Cloudwatch**: Improved error handling [#12489](https://github.com/grafana/grafana/issues/12489), thx [@mtanda](https://github.com/mtanda)
* **Cloudwatch**: AppSync metrics and dimensions [#12300](https://github.com/grafana/grafana/issues/12300), thx [@franciscocpg](https://github.com/franciscocpg)
* **Cloudwatch**: Direct Connect metrics and dimensions [#12762](https://github.com/grafana/grafana/pulls/12762), thx [@mindriot88](https://github.com/mindriot88)
* **Cloudwatch**: Added BurstBalance metric to list of AWS RDS metrics [#12561](https://github.com/grafana/grafana/pulls/12561), thx [@activeshadow](https://github.com/activeshadow)
* **Cloudwatch**: Add new Redshift metrics and dimensions [#12063](https://github.com/grafana/grafana/pulls/12063), thx [@A21z](https://github.com/A21z)
* **Table**: Adjust header contrast for the light theme [#12668](https://github.com/grafana/grafana/issues/12668)
* **Table**: Fix link color when using light theme and thresholds in use [#12766](https://github.com/grafana/grafana/issues/12766)
om/grafana/grafana/issues/12668)
* **Table**: Fix for useless horizontal scrollbar for table panel [#9964](https://github.com/grafana/grafana/issues/9964)
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
* **Elasticsearch**: For alerting/backend, support having index name to the right of pattern in index pattern [#12731](https://github.com/grafana/grafana/issues/12731)
* **OAuth**: Fix overriding tls_skip_verify_insecure using environment variable [#12747](https://github.com/grafana/grafana/issues/12747), thx [@jangaraj](https://github.com/jangaraj)
* **Units**: Change units to include characters for power of 2 and 3 [#12744](https://github.com/grafana/grafana/pull/12744), thx [@Worty](https://github.com/Worty)
* **Units**: Polish złoty currency [#12691](https://github.com/grafana/grafana/pull/12691), thx [@mwegrzynek](https://github.com/mwegrzynek)
* **Graph**: Option to hide series from tooltip [#3341](https://github.com/grafana/grafana/issues/3341), thx [@mtanda](https://github.com/mtanda)
* **UI**: Fix iOS home screen "app" icon and Windows 10 app experience [#12752](https://github.com/grafana/grafana/issues/12752), thx [@andig](https://github.com/andig)
* **Datasource**: Fix UI issue with secret fields after updating datasource [#11270](https://github.com/grafana/grafana/issues/11270)
* **Plugins**: Convert URL-like text to links in plugins readme [#12843](https://github.com/grafana/grafana/pull/12843), thx [pgiraud](https://github.com/pgiraud)
* **Docker**: Make it possible to set a specific plugin url [#12861](https://github.com/grafana/grafana/pull/12861), thx [ClementGautier](https://github.com/ClementGautier)
* **Graphite**: Fix for quoting of int function parameters (when using variables) [#11927](https://github.com/grafana/grafana/pull/11927)
* **InfluxDB**: Support timeFilter in query templating for InfluxDB [#12598](https://github.com/grafana/grafana/pull/12598), thx [kichristensen](https://github.com/kichristensen)
* **Provisioning**: Should allow one default datasource per organisation [#12229](https://github.com/grafana/grafana/issues/12229)
* **Heatmap**: Fix broken tooltip and crosshair on Firefox [#12486](https://github.com/grafana/grafana/issues/12486)
# 5.2.2 (unreleased)
### Breaking changes
* Postgres datasource no longer automatically adds time column alias when using the $__timeGroup alias. However, there's code in place which should make this change backward compatible and shouldn't create any issues.
### New experimental features
These are new features that's still being worked on and are in an experimental phase. We incourage users to try these out and provide any feedback in related issue.
* **Dashboard**: Auto fit dashboard panels to optimize space used for current TV / Monitor [#12768](https://github.com/grafana/grafana/issues/12768)
### Tech
* **Frontend**: Convert all Frontend Karma tests to Jest tests [#12224](https://github.com/grafana/grafana/issues/12224)
# 5.2.2 (2018-07-25)
### Minor
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
* **Dashboard**: Dashboard links not updated when changing variables [#12506](https://github.com/grafana/grafana/issues/12506)
* **Postgres/MySQL/MSSQL**: Fix connection leak [#12636](https://github.com/grafana/grafana/issues/12636) [#9827](https://github.com/grafana/grafana/issues/9827)
* **Plugins**: Fix loading of external plugins [#12551](https://github.com/grafana/grafana/issues/12551)
* **Dashboard**: Remove unwanted scrollbars in embedded panels [#12589](https://github.com/grafana/grafana/issues/12589)
* **Prometheus**: Prevent error using $__interval_ms in query [#12533](https://github.com/grafana/grafana/pull/12533), thx [@mtanda](https://github.com/mtanda)
# 5.2.1 (2018-06-29)

82
Dockerfile Normal file
View File

@ -0,0 +1,82 @@
# Golang build container
FROM golang:1.10
WORKDIR $GOPATH/src/github.com/grafana/grafana
COPY Gopkg.toml Gopkg.lock ./
COPY vendor vendor
ARG DEP_ENSURE=""
RUN if [ ! -z "${DEP_ENSURE}" ]; then \
go get -u github.com/golang/dep/cmd/dep && \
dep ensure --vendor-only; \
fi
COPY pkg pkg
COPY build.go build.go
COPY package.json package.json
RUN go run build.go build
# Node build container
FROM node:8
WORKDIR /usr/src/app/
COPY package.json yarn.lock ./
RUN yarn install --pure-lockfile --no-progress
COPY Gruntfile.js tsconfig.json tslint.json ./
COPY public public
COPY scripts scripts
COPY emails emails
ENV NODE_ENV production
RUN ./node_modules/.bin/grunt build
# Final container
FROM debian:stretch-slim
ARG GF_UID="472"
ARG GF_GID="472"
ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \
GF_PATHS_DATA="/var/lib/grafana" \
GF_PATHS_HOME="/usr/share/grafana" \
GF_PATHS_LOGS="/var/log/grafana" \
GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \
GF_PATHS_PROVISIONING="/etc/grafana/provisioning"
WORKDIR $GF_PATHS_HOME
RUN apt-get update && apt-get install -qq -y libfontconfig ca-certificates && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
COPY conf ./conf
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
groupadd -r -g $GF_GID grafana && \
useradd -r -u $GF_UID -g grafana grafana && \
mkdir -p "$GF_PATHS_PROVISIONING/datasources" \
"$GF_PATHS_PROVISIONING/dashboards" \
"$GF_PATHS_LOGS" \
"$GF_PATHS_PLUGINS" \
"$GF_PATHS_DATA" && \
cp "$GF_PATHS_HOME/conf/sample.ini" "$GF_PATHS_CONFIG" && \
cp "$GF_PATHS_HOME/conf/ldap.toml" /etc/grafana/ldap.toml && \
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" && \
chmod 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS"
COPY --from=0 /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-server /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-cli ./bin/
COPY --from=1 /usr/src/app/public ./public
COPY --from=1 /usr/src/app/tools ./tools
COPY tools/phantomjs/render.js ./tools/phantomjs/render.js
EXPOSE 3000
COPY ./packaging/docker/run.sh /run.sh
USER grafana
ENTRYPOINT [ "/run.sh" ]

15
Gopkg.lock generated
View File

@ -32,6 +32,7 @@
"aws/credentials/ec2rolecreds",
"aws/credentials/endpointcreds",
"aws/credentials/stscreds",
"aws/csm",
"aws/defaults",
"aws/ec2metadata",
"aws/endpoints",
@ -43,6 +44,8 @@
"internal/shareddefaults",
"private/protocol",
"private/protocol/ec2query",
"private/protocol/eventstream",
"private/protocol/eventstream/eventstreamapi",
"private/protocol/query",
"private/protocol/query/queryutil",
"private/protocol/rest",
@ -54,8 +57,8 @@
"service/s3",
"service/sts"
]
revision = "c7cd1ebe87257cde9b65112fc876b0339ea0ac30"
version = "v1.13.49"
revision = "fde4ded7becdeae4d26bf1212916aabba79349b4"
version = "v1.14.12"
[[projects]]
branch = "master"
@ -424,6 +427,12 @@
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/shurcooL/sanitized_anchor_name"
packages = ["."]
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
[[projects]]
name = "github.com/smartystreets/assertions"
packages = [
@ -670,6 +679,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "85cc057e0cc074ab5b43bd620772d63d51e07b04e8782fcfe55e6929d2fc40f7"
inputs-digest = "cb8e7fd81f23ec987fc4d5dd9d31ae0f1164bc2f30cbea2fe86e0d97dd945beb"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -36,7 +36,7 @@ ignored = [
[[constraint]]
name = "github.com/aws/aws-sdk-go"
version = "1.12.65"
version = "1.13.56"
[[constraint]]
branch = "master"

View File

@ -1,4 +1,3 @@
/* jshint node:true */
'use strict';
module.exports = function (grunt) {
var os = require('os');

View File

@ -24,6 +24,15 @@ build-js:
build: build-go build-js
build-docker-dev:
@echo "\033[92mInfo:\033[0m the frontend code is expected to be built already."
go run build.go -goos linux -pkg-arch amd64 ${OPT} build package-only latest
cp dist/grafana-latest.linux-x64.tar.gz packaging/docker
cd packaging/docker && docker build --tag grafana/grafana:dev .
build-docker-full:
docker build --tag grafana/grafana:dev .
test-go:
go test -v ./pkg/...
@ -36,4 +45,4 @@ run:
./bin/grafana-server
protoc:
protoc -I pkg/tsdb/models pkg/tsdb/models/*.proto --go_out=plugins=grpc:pkg/tsdb/models/.
protoc -I pkg/tsdb/models pkg/tsdb/models/*.proto --go_out=plugins=grpc:pkg/tsdb/models/.

View File

@ -1,5 +1,5 @@
Copyright 2014-2017 Grafana Labs
Copyright 2014-2018 Grafana Labs
This software is based on Kibana:
Copyright 2012-2013 Elasticsearch BV

View File

@ -43,7 +43,7 @@ To build the assets, rebuild on file change, and serve them by Grafana's webserv
```bash
npm install -g yarn
yarn install --pure-lockfile
npm run watch
yarn watch
```
Build the assets, rebuild on file change with Hot Module Replacement (HMR), and serve them by webpack-dev-server (http://localhost:3333):
@ -54,14 +54,9 @@ env GRAFANA_THEME=light yarn start
```
Note: HMR for Angular is not supported. If you edit files in the Angular part of the app, the whole page will reload.
Run tests
Run tests
```bash
npm run jest
```
Run karma tests
```bash
npm run karma
yarn jest
```
### Recompile backend on source change
@ -74,6 +69,15 @@ bra run
Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
### Building a docker image (on linux/amd64)
This builds a docker image from your local sources:
1. Build the frontend `go run build.go build-frontend`
2. Build the docker image `make build-docker-dev`
The resulting image will be tagged as `grafana/grafana:dev`
### Dev config
Create a custom.ini in the conf directory to override default configuration options.
@ -89,30 +93,38 @@ In your custom.ini uncomment (remove the leading `;`) sign. And set `app_mode =
#### Frontend
Execute all frontend tests
```bash
npm run test
yarn test
```
Writing & watching frontend tests (we have two test runners)
Writing & watching frontend tests
- jest for all new tests that do not require browser context (React+more)
- Start watcher: `npm run jest`
- Jest will run all test files that end with the name ".jest.ts"
- karma + mocha is used for testing angularjs components. We do want to migrate these test to jest over time (if possible).
- Start watcher: `npm run karma`
- Karma+Mocha runs all files that end with the name "_specs.ts".
- Start watcher: `yarn jest`
- Jest will run all test files that end with the name ".test.ts"
#### Backend
```bash
# Run Golang tests using sqlite3 as database (default)
go test ./pkg/...
go test ./pkg/...
# Run Golang tests using mysql as database - convenient to use /docker/blocks/mysql_tests
GRAFANA_TEST_DB=mysql go test ./pkg/...
GRAFANA_TEST_DB=mysql go test ./pkg/...
# Run Golang tests using postgres as database - convenient to use /docker/blocks/postgres_tests
GRAFANA_TEST_DB=postgres go test ./pkg/...
GRAFANA_TEST_DB=postgres go test ./pkg/...
```
## Building custom docker image
You can build a custom image using Docker, which doesn't require installing any dependencies besides docker itself.
```bash
git clone https://github.com/grafana/grafana
cd grafana
docker build -t grafana:dev .
docker run -d --name=grafana -p 3000:3000 grafana:dev
```
Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
## Contribute
If you have any idea for an improvement or found a bug, do not hesitate to open an issue.

View File

@ -1,9 +1,10 @@
# Roadmap (2018-06-26)
# Roadmap (2018-08-07)
This roadmap is a tentative plan for the core development team. Things change constantly as PRs come in and priorities change.
But it will give you an idea of our current vision and plan.
### Short term (1-2 months)
- PRs & Bugs
- Multi-Stat panel
- Metrics & Log Explore UI
@ -11,17 +12,16 @@ But it will give you an idea of our current vision and plan.
- React Panels
- Change visualization (panel type) on the fly.
- Templating Query Editor UI Plugin hook
- Backend plugins
### Long term (4 - 8 months)
- Alerting improvements (silence, per series tracking, etc)
- Progress on React migration
- Alerting improvements (silence, per series tracking, etc)
- Progress on React migration
### In a distant future far far away
- Meta queries
- Integrated light weight TSDB
- Web socket & live data sources
- Meta queries
- Integrated light weight TSDB
- Web socket & live data sources
### Outside contributions
We know this is being worked on right now by contributors (and we hope to merge it when it's ready).

View File

@ -64,6 +64,10 @@ func main() {
readVersionFromPackageJson()
if pkgArch == "" {
pkgArch = goarch
}
log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
if flag.NArg() == 0 {
@ -105,10 +109,17 @@ func main() {
case "package":
grunt(gruntBuildArg("build")...)
packageGrafana()
grunt(gruntBuildArg("package")...)
if goos == "linux" {
createLinuxPackages()
}
case "package-only":
packageGrafana()
grunt(gruntBuildArg("package")...)
if goos == "linux" {
createLinuxPackages()
}
case "pkg-rpm":
grunt(gruntBuildArg("release")...)
@ -133,22 +144,6 @@ func main() {
}
}
func packageGrafana() {
platformArg := fmt.Sprintf("--platform=%v", goos)
previousPkgArch := pkgArch
if pkgArch == "" {
pkgArch = goarch
}
postProcessArgs := gruntBuildArg("package")
postProcessArgs = append(postProcessArgs, platformArg)
grunt(postProcessArgs...)
pkgArch = previousPkgArch
if goos == "linux" {
createLinuxPackages()
}
}
func makeLatestDistCopies() {
files, err := ioutil.ReadDir("dist")
if err != nil {
@ -330,6 +325,7 @@ func createPackage(options linuxPackageOptions) {
name := "grafana"
if enterprise {
name += "-enterprise"
args = append(args, "--replaces", "grafana")
}
args = append(args, "--name", name)
@ -403,6 +399,8 @@ func gruntBuildArg(task string) []string {
if phjsToRelease != "" {
args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease))
}
args = append(args, fmt.Sprintf("--platform=%v", goos))
return args
}

View File

@ -213,6 +213,9 @@ allow_org_create = false
# Set to true to automatically assign new users to the default organization (id 1)
auto_assign_org = true
# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true)
auto_assign_org_id = 1
# Default role new users will be automatically assigned (if auto_assign_org above is set to true)
auto_assign_org_role = Viewer
@ -267,6 +270,18 @@ api_url = https://api.github.com/user
team_ids =
allowed_organizations =
#################################### GitLab Auth #########################
[auth.gitlab]
enabled = false
allow_sign_up = true
client_id = some_id
client_secret = some_secret
scopes = api
auth_url = https://gitlab.com/oauth/authorize
token_url = https://gitlab.com/oauth/token
api_url = https://gitlab.com/api/v4
allowed_groups =
#################################### Google Auth #########################
[auth.google]
enabled = false
@ -311,6 +326,10 @@ token_url =
api_url =
team_ids =
allowed_organizations =
tls_skip_verify_insecure = false
tls_client_cert =
tls_client_key =
tls_client_ca =
#################################### Basic Auth ##########################
[auth.basic]

View File

@ -15,6 +15,9 @@ start_tls = false
ssl_skip_verify = false
# set to the path to your root CA certificate or leave unset to use system defaults
# root_ca_cert = "/path/to/certificate.crt"
# Authentication against LDAP servers requiring client certificates
# client_cert = "/path/to/client.crt"
# client_key = "/path/to/client.key"
# Search user bind dn
bind_dn = "cn=admin,dc=grafana,dc=org"
@ -72,6 +75,8 @@ email = "email"
[[servers.group_mappings]]
group_dn = "cn=admins,dc=grafana,dc=org"
org_role = "Admin"
# To make user an instance admin (Grafana Admin) uncomment line below
# grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used
# org_id = 1

View File

@ -272,6 +272,10 @@ log_queries =
;api_url = https://foo.bar/user
;team_ids =
;allowed_organizations =
;tls_skip_verify_insecure = false
;tls_client_cert =
;tls_client_key =
;tls_client_ca =
#################################### Grafana.com Auth ####################
[auth.grafana_com]

View File

@ -1,11 +1,16 @@
This folder contains useful scripts and configuration for...
* Configuring datasources in Grafana
* Provision example dashboards in Grafana
* Run preconfiured datasources as docker containers
want to know more? run setup!
* Configuring dev datasources in Grafana
* Configuring dev & test scenarios dashboards.
```bash
./setup.sh
```
After restarting grafana server there should now be a number of datasources named `gdev-<type>` provisioned as well as a dashboard folder named `gdev dashboards`. This folder contains dashboard & panel features tests dashboards.
# Dev dashboards
Please update these dashboards or make new ones as new panels & dashboards features are developed or new bugs are found. The dashboards are located in the `devenv/dev-dashboards` folder.

View File

@ -5,5 +5,5 @@ providers:
folder: 'Bulk dashboards'
type: file
options:
path: devenv/dashboards/bulk-testing
path: devenv/bulk-dashboards

View File

@ -14,6 +14,9 @@ datasources:
isDefault: true
url: http://localhost:9090
- name: gdev-testdata
type: testdata
- name: gdev-influxdb
type: influxdb
access: proxy
@ -48,19 +51,46 @@ datasources:
user: grafana
password: password
- name: gdev-mysql-ds-tests
type: mysql
url: localhost:3306
database: grafana_ds_tests
user: grafana
password: password
- name: gdev-mssql
type: mssql
url: localhost:1433
database: grafana
user: grafana
password: "Password!"
secureJsonData:
password: Password!
- name: gdev-mssql-ds-tests
type: mssql
url: localhost:1433
database: grafanatest
user: grafana
secureJsonData:
password: Password!
- name: gdev-postgres
type: postgres
url: localhost:5432
database: grafana
user: grafana
password: password
secureJsonData:
password: password
jsonData:
sslmode: "disable"
- name: gdev-postgres-ds-tests
type: postgres
url: localhost:5432
database: grafanadstest
user: grafanatest
secureJsonData:
password: grafanatest
jsonData:
sslmode: "disable"
@ -71,3 +101,4 @@ datasources:
authType: credentials
defaultRegion: eu-west-2

View File

@ -1,592 +0,0 @@
{
"annotations": {
"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": 0,
"id": 59,
"links": [],
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 9,
"panels": [],
"title": "Row title",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 0,
"y": 1
},
"id": 12,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 12,
"y": 1
},
"id": 5,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 5
},
"id": 7,
"panels": [],
"title": "Row",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 0,
"y": 6
},
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 12,
"y": 6
},
"id": 13,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 10
},
"id": 11,
"panels": [],
"title": "Row title",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 0,
"y": 11
},
"id": 4,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 4,
"w": 12,
"x": 12,
"y": 11
},
"id": 3,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "go_goroutines",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"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
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 16,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-30m",
"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": "Dashboard with rows",
"uid": "1DdOzBNmk",
"version": 5
}

View File

@ -1,40 +1,4 @@
{
"__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": [
{
@ -52,8 +16,7 @@
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1520976748896,
"iteration": 1532618661457,
"links": [],
"panels": [
{
@ -63,7 +26,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"fill": 2,
"gridPos": {
"h": 9,
@ -149,14 +112,18 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"fill": 2,
"gridPos": {
"h": 18,
@ -234,14 +201,18 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"fill": 2,
"gridPos": {
"h": 9,
@ -313,11 +284,15 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"columns": [],
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"fontSize": "100%",
"gridPos": {
"h": 10,
@ -371,13 +346,13 @@
],
"schemaVersion": 16,
"style": "dark",
"tags": [],
"tags": ["gdev", "mssql", "fake-data-gen"],
"templating": {
"list": [
{
"allValue": null,
"current": {},
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"hide": 0,
"includeAll": false,
"label": "Datacenter",
@ -387,6 +362,7 @@
"query": "SELECT DISTINCT datacenter FROM grafana_metric",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -397,7 +373,7 @@
{
"allValue": null,
"current": {},
"datasource": "${DS_MSSQL}",
"datasource": "gdev-mssql",
"hide": 0,
"includeAll": true,
"label": "Hostname",
@ -407,6 +383,7 @@
"query": "SELECT DISTINCT hostname FROM grafana_metric WHERE datacenter='$datacenter'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -499,6 +476,7 @@
],
"query": "1s,10s,30s,1m,5m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
"refresh": 2,
"skipUrlSync": false,
"type": "interval"
}
]
@ -533,7 +511,7 @@
]
},
"timezone": "",
"title": "Grafana Fake Data Gen - MSSQL",
"title": "Datasource tests - MSSQL",
"uid": "86Js1xRmk",
"version": 11
"version": 1
}

View File

@ -1,40 +1,4 @@
{
"__inputs": [
{
"name": "DS_MYSQL",
"label": "MySQL",
"description": "",
"type": "datasource",
"pluginId": "mysql",
"pluginName": "MySQL"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "5.0.0"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": "5.0.0"
},
{
"type": "datasource",
"id": "mysql",
"name": "MySQL",
"version": "5.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"version": "5.0.0"
}
],
"annotations": {
"list": [
{
@ -52,8 +16,7 @@
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1523372133566,
"iteration": 1532620738041,
"links": [],
"panels": [
{
@ -63,7 +26,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MYSQL}",
"datasource": "gdev-mysql",
"fill": 2,
"gridPos": {
"h": 9,
@ -161,7 +124,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MYSQL}",
"datasource": "gdev-mysql",
"fill": 2,
"gridPos": {
"h": 18,
@ -251,7 +214,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_MYSQL}",
"datasource": "gdev-mysql",
"fill": 2,
"gridPos": {
"h": 9,
@ -332,7 +295,7 @@
},
{
"columns": [],
"datasource": "${DS_MYSQL}",
"datasource": "gdev-mysql",
"fontSize": "100%",
"gridPos": {
"h": 9,
@ -390,6 +353,7 @@
"schemaVersion": 16,
"style": "dark",
"tags": [
"gdev",
"fake-data-gen",
"mysql"
],
@ -397,8 +361,11 @@
"list": [
{
"allValue": null,
"current": {},
"datasource": "${DS_MYSQL}",
"current": {
"text": "America",
"value": "America"
},
"datasource": "gdev-mysql",
"hide": 0,
"includeAll": false,
"label": "Datacenter",
@ -408,6 +375,7 @@
"query": "SELECT DISTINCT datacenter FROM grafana_metric",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -417,8 +385,11 @@
},
{
"allValue": null,
"current": {},
"datasource": "${DS_MYSQL}",
"current": {
"text": "All",
"value": "$__all"
},
"datasource": "gdev-mysql",
"hide": 0,
"includeAll": true,
"label": "Hostname",
@ -428,6 +399,7 @@
"query": "SELECT DISTINCT hostname FROM grafana_metric WHERE datacenter='$datacenter'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -520,6 +492,7 @@
],
"query": "1s,10s,30s,1m,5m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
"refresh": 2,
"skipUrlSync": false,
"type": "interval"
}
]
@ -554,7 +527,7 @@
]
},
"timezone": "",
"title": "Grafana Fake Data Gen - MySQL",
"title": "Datasource tests - MySQL",
"uid": "DGsCac3kz",
"version": 8
}

View File

@ -1,40 +1,4 @@
{
"__inputs": [
{
"name": "DS_POSTGRESQL",
"label": "PostgreSQL",
"description": "",
"type": "datasource",
"pluginId": "postgres",
"pluginName": "PostgreSQL"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "5.0.0"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "datasource",
"id": "postgres",
"name": "PostgreSQL",
"version": "1.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"version": ""
}
],
"annotations": {
"list": [
{
@ -52,8 +16,7 @@
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1518601837383,
"iteration": 1532620601931,
"links": [],
"panels": [
{
@ -63,7 +26,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_POSTGRESQL}",
"datasource": "gdev-postgres",
"fill": 2,
"gridPos": {
"h": 9,
@ -150,14 +113,18 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_POSTGRESQL}",
"datasource": "gdev-postgres",
"fill": 2,
"gridPos": {
"h": 18,
@ -236,14 +203,18 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_POSTGRESQL}",
"datasource": "gdev-postgres",
"fill": 2,
"gridPos": {
"h": 9,
@ -316,11 +287,15 @@
"min": null,
"show": true
}
]
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"columns": [],
"datasource": "${DS_POSTGRESQL}",
"datasource": "gdev-postgres",
"fontSize": "100%",
"gridPos": {
"h": 9,
@ -377,6 +352,7 @@
"schemaVersion": 16,
"style": "dark",
"tags": [
"gdev",
"fake-data-gen",
"postgres"
],
@ -384,8 +360,11 @@
"list": [
{
"allValue": null,
"current": {},
"datasource": "${DS_POSTGRESQL}",
"current": {
"text": "America",
"value": "America"
},
"datasource": "gdev-postgres",
"hide": 0,
"includeAll": false,
"label": "Datacenter",
@ -395,6 +374,7 @@
"query": "SELECT DISTINCT datacenter FROM grafana_metric",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -404,8 +384,11 @@
},
{
"allValue": null,
"current": {},
"datasource": "${DS_POSTGRESQL}",
"current": {
"text": "All",
"value": "$__all"
},
"datasource": "gdev-postgres",
"hide": 0,
"includeAll": true,
"label": "Hostname",
@ -415,6 +398,7 @@
"query": "SELECT DISTINCT hostname FROM grafana_metric WHERE datacenter='$datacenter'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@ -507,6 +491,7 @@
],
"query": "1s,10s,30s,1m,5m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
"refresh": 2,
"skipUrlSync": false,
"type": "interval"
}
]
@ -541,7 +526,7 @@
]
},
"timezone": "",
"title": "Grafana Fake Data Gen - PostgreSQL",
"title": "Datasource tests - Postgres",
"uid": "JYola5qzz",
"version": 1
"version": 4
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,574 @@
{
"annotations": {
"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": 0,
"links": [],
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": true,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 7,
"w": 8,
"x": 0,
"y": 0
},
"id": 2,
"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": "postfix",
"postfixFontSize": "50%",
"prefix": "prefix",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": true
},
"tableColumn": "",
"targets": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,2,3,4,5"
}
],
"thresholds": "5,10",
"title": "prefix 3 ms (green) postfixt + sparkline",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorPrefix": false,
"colorValue": true,
"colors": [
"#d44a3a",
"rgba(237, 129, 40, 0.89)",
"#299c46"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 7,
"w": 8,
"x": 8,
"y": 0
},
"id": 3,
"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": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,2,3,4,5"
}
],
"thresholds": "5,10",
"title": "3 ms (red) + full height sparkline",
"type": "singlestat",
"valueFontSize": "200%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorPrefix": false,
"colorValue": false,
"colors": [
"#d44a3a",
"rgba(237, 129, 40, 0.89)",
"#299c46"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 7,
"w": 8,
"x": 16,
"y": 0
},
"id": 4,
"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": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,2,3,4,5"
}
],
"thresholds": "5,10",
"title": "3 ms + red background",
"type": "singlestat",
"valueFontSize": "200%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorPrefix": false,
"colorValue": true,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 150,
"minValue": 0,
"show": true,
"thresholdLabels": true,
"thresholdMarkers": true
},
"gridPos": {
"h": 7,
"w": 8,
"x": 0,
"y": 7
},
"id": 5,
"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": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "10,20,80"
}
],
"thresholds": "81,90",
"title": "80 ms green gauge, thresholds 81, 90",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorPrefix": false,
"colorValue": true,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 150,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 7,
"w": 8,
"x": 8,
"y": 7
},
"id": 6,
"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": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "10,20,80"
}
],
"thresholds": "81,90",
"title": "80 ms green gauge, thresholds 81, 90, no labels",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorPrefix": false,
"colorValue": true,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": "gdev-testdata",
"decimals": null,
"description": "",
"format": "ms",
"gauge": {
"maxValue": 150,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": false
},
"gridPos": {
"h": 7,
"w": 8,
"x": 16,
"y": 7
},
"id": 7,
"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": [
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "10,20,80"
}
],
"thresholds": "81,90",
"title": "80 ms green gauge, thresholds 81, 90, no markers or labels",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
}
],
"refresh": false,
"revision": 8,
"schemaVersion": 16,
"style": "dark",
"tags": [
"gdev",
"panel-tests"
],
"templating": {
"list": []
},
"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": "browser",
"title": "Panel Tests - Singlestat",
"uid": "singlestat",
"version": 14
}

View File

@ -0,0 +1,453 @@
{
"annotations": {
"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": 0,
"links": [],
"panels": [
{
"columns": [],
"datasource": "gdev-testdata",
"fontSize": "100%",
"gridPos": {
"h": 11,
"w": 12,
"x": 0,
"y": 0
},
"id": 3,
"links": [],
"pageSize": 10,
"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": "cell",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorCell",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "currencyUSD"
},
{
"alias": "",
"colorMode": "value",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorValue",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "Bps"
},
{
"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": "server1",
"expr": "",
"format": "table",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0,20,10"
},
{
"alias": "server2",
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0"
}
],
"title": "Time series to rows (2 pages)",
"transform": "timeseries_to_rows",
"type": "table"
},
{
"columns": [
{
"text": "Avg",
"value": "avg"
},
{
"text": "Max",
"value": "max"
},
{
"text": "Current",
"value": "current"
}
],
"datasource": "gdev-testdata",
"fontSize": "100%",
"gridPos": {
"h": 11,
"w": 12,
"x": 12,
"y": 0
},
"id": 4,
"links": [],
"pageSize": 10,
"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": "cell",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorCell",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "currencyUSD"
},
{
"alias": "",
"colorMode": "value",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorValue",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "Bps"
},
{
"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": "server1",
"expr": "",
"format": "table",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0,20,10"
},
{
"alias": "server2",
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0"
}
],
"title": "Time series aggregations",
"transform": "timeseries_aggregations",
"type": "table"
},
{
"columns": [],
"datasource": "gdev-testdata",
"fontSize": "100%",
"gridPos": {
"h": 7,
"w": 24,
"x": 0,
"y": 11
},
"id": 5,
"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": "row",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "/Color/",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "currencyUSD"
},
{
"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": "ColorValue",
"expr": "",
"format": "table",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0,20,10"
}
],
"title": "color row by threshold",
"transform": "timeseries_to_columns",
"type": "table"
},
{
"columns": [],
"datasource": "gdev-testdata",
"fontSize": "100%",
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 18
},
"id": 2,
"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": "cell",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorCell",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "currencyUSD"
},
{
"alias": "",
"colorMode": "value",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "ColorValue",
"thresholds": [
"5",
"10"
],
"type": "number",
"unit": "Bps"
},
{
"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": "ColorValue",
"expr": "",
"format": "table",
"intervalFactor": 1,
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "1,20,90,30,5,0,20,10"
},
{
"alias": "ColorCell",
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "5,1,2,3,4,5,10,20"
}
],
"title": "Column style thresholds & units",
"transform": "timeseries_to_columns",
"type": "table"
}
],
"refresh": false,
"revision": 8,
"schemaVersion": 16,
"style": "dark",
"tags": [
"gdev",
"panel-tests"
],
"templating": {
"list": []
},
"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": "browser",
"title": "Panel Tests - Table",
"uid": "pttable",
"version": 1
}

View File

@ -1,6 +1,6 @@
{
"revision": 2,
"title": "TestData - Alerts",
"title": "Alerting with TestData",
"tags": [
"grafana-test"
],
@ -48,7 +48,7 @@
},
"aliasColors": {},
"bars": false,
"datasource": "Grafana TestData",
"datasource": "gdev-testdata",
"editable": true,
"error": false,
"fill": 1,
@ -161,7 +161,7 @@
},
"aliasColors": {},
"bars": false,
"datasource": "Grafana TestData",
"datasource": "gdev-testdata",
"editable": true,
"error": false,
"fill": 1,

View File

@ -1,4 +1,4 @@
#/bin/bash
#!/bin/bash
bulkDashboard() {
@ -7,11 +7,11 @@ bulkDashboard() {
COUNTER=0
MAX=400
while [ $COUNTER -lt $MAX ]; do
jsonnet -o "dashboards/bulk-testing/dashboard${COUNTER}.json" -e "local bulkDash = import 'dashboards/bulk-testing/bulkdash.jsonnet'; bulkDash + { uid: 'uid-${COUNTER}', title: 'title-${COUNTER}' }"
jsonnet -o "bulk-dashboards/dashboard${COUNTER}.json" -e "local bulkDash = import 'bulk-dashboards/bulkdash.jsonnet'; bulkDash + { uid: 'uid-${COUNTER}', title: 'title-${COUNTER}' }"
let COUNTER=COUNTER+1
done
ln -s -f -r ./dashboards/bulk-testing/bulk-dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
ln -s -f -r ./bulk-dashboards/bulk-dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
}
requiresJsonnet() {
@ -22,31 +22,37 @@ requiresJsonnet() {
fi
}
defaultDashboards() {
devDashboards() {
echo -e "\xE2\x9C\x94 Setting up all dev dashboards using provisioning"
ln -s -f ../../../devenv/dashboards.yaml ../conf/provisioning/dashboards/dev.yaml
}
defaultDatasources() {
echo "setting up all default datasources using provisioning"
devDatasources() {
echo -e "\xE2\x9C\x94 Setting up all dev datasources using provisioning"
ln -s -f ../../../devenv/datasources.yaml ../conf/provisioning/datasources/dev.yaml
}
usage() {
echo -e "install.sh\n\tThis script setups dev provision for datasources and dashboards"
echo -e "\n"
echo "Usage:"
echo " bulk-dashboards - create and provisioning 400 dashboards"
echo " no args - provisiong core datasources and dev dashboards"
}
main() {
echo -e "------------------------------------------------------------------"
echo -e "This script setups provisioning for dev datasources and dashboards"
echo -e "------------------------------------------------------------------"
echo -e "\n"
local cmd=$1
if [[ $cmd == "bulk-dashboards" ]]; then
bulkDashboard
else
defaultDashboards
defaultDatasources
devDashboards
devDatasources
fi
if [[ -z "$cmd" ]]; then

View File

@ -1,3 +1,4 @@
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/nginx.conf
COPY htpasswd /etc/nginx/htpasswd

View File

@ -0,0 +1,3 @@
user1:$apr1$1odeeQb.$kwV8D/VAAGUDU7pnHuKoV0
user2:$apr1$A2kf25r.$6S0kp3C7vIuixS5CL0XA9.
admin:$apr1$IWn4DoRR$E2ol7fS/dkI18eU4bXnBO1

View File

@ -13,7 +13,26 @@ http {
listen 10080;
location /grafana/ {
################################################################
# Enable these settings to test with basic auth and an auth proxy header
# the htpasswd file contains an admin user with password admin and
# user1: grafana and user2: grafana
################################################################
# auth_basic "Restricted Content";
# auth_basic_user_file /etc/nginx/htpasswd;
################################################################
# To use the auth proxy header, set the following in custom.ini:
# [auth.proxy]
# enabled = true
# header_name = X-WEBAUTH-USER
# header_property = username
################################################################
# proxy_set_header X-WEBAUTH-USER $remote_user;
proxy_pass http://localhost:3000/;
}
}
}
}

View File

@ -0,0 +1,85 @@
# To troubleshoot and get more log info enable ldap debug logging in grafana.ini
# [log]
# filters = ldap:debug
[[servers]]
# Ldap server host (specify multiple hosts space separated)
host = "127.0.0.1"
# Default port is 389 or 636 if use_ssl = true
port = 389
# Set to true if ldap server supports TLS
use_ssl = false
# Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS)
start_tls = false
# set to true if you want to skip ssl cert validation
ssl_skip_verify = false
# set to the path to your root CA certificate or leave unset to use system defaults
# root_ca_cert = "/path/to/certificate.crt"
# Search user bind dn
bind_dn = "cn=admin,dc=grafana,dc=org"
# Search user bind password
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
bind_password = 'grafana'
# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"
search_filter = "(cn=%s)"
# An array of base dns to search through
search_base_dns = ["dc=grafana,dc=org"]
# In POSIX LDAP schemas, without memberOf attribute a secondary query must be made for groups.
# This is done by enabling group_search_filter below. You must also set member_of= "cn"
# in [servers.attributes] below.
# Users with nested/recursive group membership and an LDAP server that supports LDAP_MATCHING_RULE_IN_CHAIN
# can set group_search_filter, group_search_filter_user_attribute, group_search_base_dns and member_of
# below in such a way that the user's recursive group membership is considered.
#
# Nested Groups + Active Directory (AD) Example:
#
# AD groups store the Distinguished Names (DNs) of members, so your filter must
# recursively search your groups for the authenticating user's DN. For example:
#
# group_search_filter = "(member:1.2.840.113556.1.4.1941:=%s)"
# group_search_filter_user_attribute = "distinguishedName"
# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
#
# [servers.attributes]
# ...
# member_of = "distinguishedName"
## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available)
# group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"
## Group search filter user attribute defines what user attribute gets substituted for %s in group_search_filter.
## Defaults to the value of username in [server.attributes]
## Valid options are any of your values in [servers.attributes]
## If you are using nested groups you probably want to set this and member_of in
## [servers.attributes] to "distinguishedName"
# group_search_filter_user_attribute = "distinguishedName"
## An array of the base DNs to search through for groups. Typically uses ou=groups
# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
# Specify names of the ldap attributes your ldap uses
[servers.attributes]
name = "givenName"
surname = "sn"
username = "cn"
member_of = "memberOf"
email = "email"
# Map ldap groups to grafana org roles
[[servers.group_mappings]]
group_dn = "cn=admins,ou=groups,dc=grafana,dc=org"
org_role = "Admin"
# The Grafana organization database id, optional, if left out the default org (id 1) will be used
# org_id = 1
[[servers.group_mappings]]
group_dn = "cn=editors,ou=groups,dc=grafana,dc=org"
org_role = "Editor"
[[servers.group_mappings]]
# If you want to match all (or no ldap groups) then you can use wildcard
group_dn = "*"
org_role = "Viewer"

View File

@ -14,12 +14,12 @@ After adding ldif files to `prepopulate`:
## Enabling LDAP in Grafana
The default `ldap.toml` file in `conf` has host set to `127.0.0.1` and port to set to 389 so all you need to do is enable it in the .ini file to get Grafana to use this block:
Copy the ldap_dev.toml file in this folder into your `conf` folder (it is gitignored already). To enable it in the .ini file to get Grafana to use this block:
```ini
[auth.ldap]
enabled = true
config_file = conf/ldap.toml
config_file = conf/ldap_dev.toml
; allow_sign_up = true
```
@ -43,6 +43,3 @@ editors
no groups
ldap-viewer

View File

@ -130,7 +130,7 @@ There are a couple of configuration options which need to be set up in Grafana U
Once these two properties are set, you can send the alerts to Kafka for further processing or throttling.
### All supported notifier
### All supported notifiers
Name | Type |Support images
-----|------------ | ------
@ -148,6 +148,7 @@ Pushover | `pushover` | no
Telegram | `telegram` | no
Line | `line` | no
Prometheus Alertmanager | `prometheus-alertmanager` | no
Microsoft Teams | `teams` | yes

View File

@ -115,6 +115,8 @@ and `dimension keys/values`.
In place of `region` you can specify `default` to use the default region configured in the datasource for the query,
e.g. `metrics(AWS/DynamoDB, default)` or `dimension_values(default, ..., ..., ...)`.
Read more about the available dimensions in the [CloudWatch Metrics and Dimensions Reference](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CW_Support_For_AWS.html).
Name | Description
------- | --------
*regions()* | Returns a list of regions AWS provides their service.

View File

@ -58,8 +58,8 @@ a time pattern for the index name or a wildcard.
### Elasticsearch version
Be sure to specify your Elasticsearch version in the version selection dropdown. This is very important as there are differences how queries are composed. Currently only 2.x and 5.x
are supported.
Be sure to specify your Elasticsearch version in the version selection dropdown. This is very important as there are differences how queries are composed.
Currently the versions available is 2.x, 5.x and 5.6+ where 5.6+ means a version of 5.6 or higher, 6.3.2 for example.
### Min time interval
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example `1m` if your data is written every minute.
@ -115,7 +115,7 @@ The Elasticsearch data source supports two types of queries you can use in the *
Query | Description
------------ | -------------
*{"find": "fields", "type": "keyword"} | Returns a list of field names with the index type `keyword`.
*{"find": "fields", "type": "keyword"}* | Returns a list of field names with the index type `keyword`.
*{"find": "terms", "field": "@hostname", "size": 1000}* | Returns a list of values for a field using term aggregation. Query will user current dashboard time range as time range for query.
*{"find": "terms", "field": "@hostname", "query": '<lucene query>'}* | Returns a list of values for a field using term aggregation & and a specified lucene query filter. Query will use current dashboard time range as time range for query.

View File

@ -81,10 +81,15 @@ Macro example | Description
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *'2017-04-21T05:01:17Z'*
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *'2017-04-21T05:06:17Z'*
*$__timeGroup(dateColumn,'5m'[, fillvalue])* | Will be replaced by an expression usable in GROUP BY clause. Providing a *fillValue* of *NULL* or *floating value* will automatically fill empty series in timerange with that value. <br/>For example, *CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)\*300*.
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example).
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so missing points in that series will be added by grafana and 0 will be used as value.
*$__timeGroup(dateColumn,'5m', NULL)* | Same as above but NULL will be used as value for missing points.
*$__timeGroup(dateColumn,'5m', previous)* | Same as above but the previous value in that series will be used as fill value if no value has been seen yet NULL will be used (only available in Grafana 5.3+).
*$__timeGroupAlias(dateColumn,'5m')* | Will be replaced identical to $__timeGroup but with an added column alias (only available in Grafana 5.3+).
*$__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*
*$__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+).
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
@ -148,7 +153,8 @@ The resulting table panel:
## Time series queries
If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must must have a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch in seconds. You may return a column named `metric` that is used as metric name for the value column. Any column except `time` and `metric` is treated as a value column. If you omit the `metric` column, tha name of the value column will be the metric name. You may select multiple value columns, each will have its name as metric.
If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must must have a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch in seconds. You may return a column named `metric` that is used as metric name for the value column. Any column except `time` and `metric` is treated as a value column. If you omit the `metric` column, the name of the value column will be the metric name. You may select multiple value columns, each will have its name as metric.
If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+).
**Example database table:**

View File

@ -64,10 +64,15 @@ Macro example | Description
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *'2017-04-21T05:01:17Z'*
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *'2017-04-21T05:06:17Z'*
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),*
*$__timeGroup(dateColumn,'5m',0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example).
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so missing points in that series will be added by grafana and 0 will be used as value.
*$__timeGroup(dateColumn,'5m', NULL)* | Same as above but NULL will be used as value for missing points.
*$__timeGroup(dateColumn,'5m', previous)* | Same as above but the previous value in that series will be used as fill value if no value has been seen yet NULL will be used (only available in Grafana 5.3+).
*$__timeGroupAlias(dateColumn,'5m')* | Will be replaced identical to $__timeGroup but with an added column alias (only available in Grafana 5.3+).
*$__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*
*$__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+).
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
@ -104,6 +109,7 @@ The resulting table panel:
If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must return a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch.
Any column except `time` and `metric` is treated as a value column.
You may return a column named `metric` that is used as metric name for the value column.
If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+).
**Example with `metric` column:**

View File

@ -31,6 +31,7 @@ Name | Description
*User* | Database user's login/username
*Password* | Database user's password
*SSL Mode* | This option determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server.
*TimescaleDB* | With this option enabled Grafana will use TimescaleDB features, e.g. use ```time_bucket``` for grouping by time (only available in Grafana 5.3+).
### Database User Permissions (Important!)
@ -60,11 +61,16 @@ Macro example | Description
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:06:17Z'*
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *'2017-04-21T05:01:17Z'*
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *'2017-04-21T05:06:17Z'*
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from dateColumn)/300)::bigint*300 AS time*
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example).
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from dateColumn)/300)::bigint*300*
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so missing points in that series will be added by grafana and 0 will be used as value.
*$__timeGroup(dateColumn,'5m', NULL)* | Same as above but NULL will be used as value for missing points.
*$__timeGroup(dateColumn,'5m', previous)* | Same as above but the previous value in that series will be used as fill value if no value has been seen yet NULL will be used (only available in Grafana 5.3+).
*$__timeGroupAlias(dateColumn,'5m')* | Will be replaced identical to $__timeGroup but with an added column alias (only available in Grafana 5.3+).
*$__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*
*$__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+).
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
@ -102,6 +108,7 @@ The resulting table panel:
If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must return a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch.
Any column except `time` and `metric` is treated as a value column.
You may return a column named `metric` that is used as metric name for the value column.
If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+).
**Example with `metric` column:**
@ -285,4 +292,5 @@ datasources:
password: "Password!"
jsonData:
sslmode: "disable" # disable/require/verify-ca/verify-full
timescaledb: false
```

View File

@ -75,6 +75,32 @@ Name | Description
For details of *metric names*, *label names* and *label values* are please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels).
#### Using interval and range variables
> Support for `$__range`, `$__range_s` and `$__range_ms` only available from Grafana v5.3
It's possible to use some global built-in variables in query variables; `$__interval`, `$__interval_ms`, `$__range`, `$__range_s` and `$__range_ms`, see [Global built-in variables](/reference/templating/#global-built-in-variables) for more information. These can be convenient to use in conjunction with the `query_result` function when you need to filter variable queries since
`label_values` function doesn't support queries.
Make sure to set the variable's `refresh` trigger to be `On Time Range Change` to get the correct instances when changing the time range on the dashboard.
**Example usage:**
Populate a variable with the the busiest 5 request instances based on average QPS over the time range shown in the dashboard:
```
Query: query_result(topk(5, sum(rate(http_requests_total[$__range])) by (instance)))
Regex: /"([^"]+)"/
```
Populate a variable with the instances having a certain state over the time range shown in the dashboard, using the more precise `$__range_s`:
```
Query: query_result(max_over_time(<metric>[${__range_s}s]) != <state>)
Regex:
```
### Using variables in queries
There are two syntaxes:

View File

@ -54,7 +54,7 @@ We utilize a unit abstraction so that Grafana looks great on all screens both sm
> Note: With MaxDataPoint functionality, Grafana can show you the perfect amount of datapoints no matter your resolution or time-range.
Utilize the [Repeating Row functionality](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) to dynamically create or remove entire Rows (that can be filled with Panels), based on the Template variables selected.
Utilize the [Repeating Rows functionality](/reference/templating/#repeating-rows) to dynamically create or remove entire Rows (that can be filled with Panels), based on the Template variables selected.
Rows can be collapsed by clicking on the Row Title. If you save a Dashboard with a Row collapsed, it will save in that state and will not preload those graphs until the row is expanded.
@ -72,7 +72,7 @@ Panels like the [Graph](/reference/graph/) panel allow you to graph as many metr
Panels can be made more dynamic by utilizing [Dashboard Templating](/reference/templating/) variable strings within the panel configuration (including queries to your Data Source configured via the Query Editor).
Utilize the [Repeating Panel](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) functionality to dynamically create or remove Panels based on the [Templating Variables](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) selected.
Utilize the [Repeating Panel](/reference/templating/#repeating-panels) functionality to dynamically create or remove Panels based on the [Templating Variables](/reference/templating/#repeating-panels) selected.
The time range on Panels is normally what is set in the [Dashboard time picker](/reference/timerange/) but this can be overridden by utilizes [Panel specific time overrides](/reference/timerange/#panel-time-overrides-timeshift).

View File

@ -59,7 +59,6 @@ Content-Type: application/json
"panelId": 1,
"name": "fire place sensor",
"state": "alerting",
"message": "Someone is trying to break in through the fire place",
"newStateDate": "2018-05-14T05:55:20+02:00",
"evalDate": "0001-01-01T00:00:00Z",
"evalData": null,

View File

@ -85,7 +85,7 @@ Status Codes:
- **403** Access denied
- **412** Precondition failed
The **412** status code is used for explaing that you cannot create the dashboard and why.
The **412** status code is used for explaining that you cannot create the dashboard and why.
There can be different reasons for this:
- The dashboard has been changed by someone else, `status=version-mismatch`

View File

@ -223,7 +223,7 @@ Status Codes:
- **404** Folder not found
- **412** Precondition failed
The **412** status code is used for explaing that you cannot update the folder and why.
The **412** status code is used for explaining that you cannot update the folder and why.
There can be different reasons for this:
- The folder has been changed by someone else, `status=version-mismatch`

View File

@ -0,0 +1,286 @@
+++
title = "Playlist HTTP API "
description = "Playlist Admin HTTP API"
keywords = ["grafana", "http", "documentation", "api", "playlist"]
aliases = ["/http_api/playlist/"]
type = "docs"
[menu.docs]
name = "Playlist"
parent = "http_api"
+++
# Playlist API
## Search Playlist
`GET /api/playlists`
Get all existing playlist for the current organization using pagination
**Example Request**:
```bash
GET /api/playlists HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
Querystring Parameters:
These parameters are used as querystring parameters.
- **query** - Limit response to playlist having a name like this value.
- **limit** - Limit response to *X* number of playlist.
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"name": "my playlist",
"interval": "5m"
}
]
```
## Get one playlist
`GET /api/playlists/:id`
**Example Request**:
```bash
GET /api/playlists/1 HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
{
"id" : 1,
"name": "my playlist",
"interval": "5m",
"orgId": "my org",
"items": [
{
"id": 1,
"playlistId": 1,
"type": "dashboard_by_id",
"value": "3",
"order": 1,
"title":"my third dasboard"
},
{
"id": 2,
"playlistId": 1,
"type": "dashboard_by_tag",
"value": "myTag",
"order": 2,
"title":"my other dasboard"
}
]
}
```
## Get Playlist items
`GET /api/playlists/:id/items`
**Example Request**:
```bash
GET /api/playlists/1/items HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"playlistId": 1,
"type": "dashboard_by_id",
"value": "3",
"order": 1,
"title":"my third dasboard"
},
{
"id": 2,
"playlistId": 1,
"type": "dashboard_by_tag",
"value": "myTag",
"order": 2,
"title":"my other dasboard"
}
]
```
## Get Playlist dashboards
`GET /api/playlists/:id/dashboards`
**Example Request**:
```bash
GET /api/playlists/1/dashboards HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 3,
"title": "my third dasboard",
"order": 1,
},
{
"id": 5,
"title":"my other dasboard"
"order": 2,
}
]
```
## Create a playlist
`POST /api/playlists/`
**Example Request**:
```bash
PUT /api/playlists/1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"name": "my playlist",
"interval": "5m",
"items": [
{
"type": "dashboard_by_id",
"value": "3",
"order": 1,
"title":"my third dasboard"
},
{
"type": "dashboard_by_tag",
"value": "myTag",
"order": 2,
"title":"my other dasboard"
}
]
}
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
{
"id": 1,
"name": "my playlist",
"interval": "5m"
}
```
## Update a playlist
`PUT /api/playlists/:id`
**Example Request**:
```bash
PUT /api/playlists/1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"name": "my playlist",
"interval": "5m",
"items": [
{
"playlistId": 1,
"type": "dashboard_by_id",
"value": "3",
"order": 1,
"title":"my third dasboard"
},
{
"playlistId": 1,
"type": "dashboard_by_tag",
"value": "myTag",
"order": 2,
"title":"my other dasboard"
}
]
}
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
{
"id" : 1,
"name": "my playlist",
"interval": "5m",
"orgId": "my org",
"items": [
{
"id": 1,
"playlistId": 1,
"type": "dashboard_by_id",
"value": "3",
"order": 1,
"title":"my third dasboard"
},
{
"id": 2,
"playlistId": 1,
"type": "dashboard_by_tag",
"value": "myTag",
"order": 2,
"title":"my other dasboard"
}
]
}
```
## Delete a playlist
`DELETE /api/playlists/:id`
**Example Request**:
```bash
DELETE /api/playlists/1 HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```json
HTTP/1.1 200
Content-Type: application/json
{}
```

View File

@ -363,6 +363,39 @@ Content-Type: application/json
]
```
## Teams that the actual User is member of
`GET /api/user/teams`
Return a list of all teams that the current user is member of.
**Example Request**:
```http
GET /api/user/teams HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"orgId": 1,
"name": "MyTestTeam",
"email": "",
"avatarUrl": "\/avatar\/3f49c15916554246daa714b9bd0ee398",
"memberCount": 1
}
]
```
## Star a dashboard
`POST /api/user/stars/dashboard/:dashboardId`

View File

@ -15,6 +15,8 @@ weight = 1
The Grafana back-end has a number of configuration options that can be
specified in a `.ini` configuration file or specified using environment variables.
> **Note.** Grafana needs to be restarted for any configuration changes to take effect.
## Comments In .ini Files
Semicolons (the `;` char) are the standard way to comment out lines in a `.ini` file.
@ -82,7 +84,7 @@ command line in the init.d script or the systemd service file.
### temp_data_lifetime
How long temporary images in `data` directory should be kept. Defaults to: `24h`. Supported modifiers: `h` (hours),
How long temporary images in `data` directory should be kept. Defaults to: `24h`. Supported modifiers: `h` (hours),
`m` (minutes), for example: `168h`, `30m`, `10h30m`. Use `0` to never clean up temporary files.
### logs
@ -179,7 +181,7 @@ embedded database (included in the main Grafana binary).
### url
Use either URL or or the other fields below to configure the database
Use either URL or the other fields below to configure the database
Example: `mysql://user:secret@host:port/database`
### type
@ -193,9 +195,9 @@ will be stored.
### host
Only applicable to MySQL or Postgres. Includes IP or hostname and port.
Only applicable to MySQL or Postgres. Includes IP or hostname and port or in case of unix sockets the path to it.
For example, for MySQL running on the same host as Grafana: `host =
127.0.0.1:3306`
127.0.0.1:3306` or with unix sockets: `host = /var/run/mysqld/mysqld.sock`
### name
@ -296,6 +298,12 @@ Set to `true` to automatically add new users to the main organization
(id 1). When set to `false`, new users will automatically cause a new
organization to be created for that new user.
### auto_assign_org_id
Set this value to automatically add new users to the provided org.
This requires `auto_assign_org` to be set to `true`. Please make sure
that this organization does already exists.
### auto_assign_org_role
The role new users will be assigned for the main organization (if the
@ -422,6 +430,108 @@ allowed_organizations = github google
<hr>
## [auth.gitlab]
> Only available in Grafana v5.3+.
You need to [create a GitLab OAuth
application](https://docs.gitlab.com/ce/integration/oauth_provider.html).
Choose a descriptive *Name*, and use the following *Redirect URI*:
```
https://grafana.example.com/login/gitlab
```
where `https://grafana.example.com` is the URL you use to connect to Grafana.
Adjust it as needed if you don't use HTTPS or if you use a different port; for
instance, if you access Grafana at `http://203.0.113.31:3000`, you should use
```
http://203.0.113.31:3000/login/gitlab
```
Finally, select *api* as the *Scope* and submit the form. Note that if you're
not going to use GitLab groups for authorization (i.e. not setting
`allowed_groups`, see below), you can select *read_user* instead of *api* as
the *Scope*, thus giving a more restricted access to your GitLab API.
You'll get an *Application Id* and a *Secret* in return; we'll call them
`GITLAB_APPLICATION_ID` and `GITLAB_SECRET` respectively for the rest of this
section.
Add the following to your Grafana configuration file to enable GitLab
authentication:
```ini
[auth.gitlab]
enabled = false
allow_sign_up = false
client_id = GITLAB_APPLICATION_ID
client_secret = GITLAB_SECRET
scopes = api
auth_url = https://gitlab.com/oauth/authorize
token_url = https://gitlab.com/oauth/token
api_url = https://gitlab.com/api/v4
allowed_groups =
```
Restart the Grafana backend for your changes to take effect.
If you use your own instance of GitLab instead of `gitlab.com`, adjust
`auth_url`, `token_url` and `api_url` accordingly by replacing the `gitlab.com`
hostname with your own.
With `allow_sign_up` set to `false`, only existing users will be able to login
using their GitLab account, but with `allow_sign_up` set to `true`, *any* user
who can authenticate on GitLab will be able to login on your Grafana instance;
if you use the public `gitlab.com`, it means anyone in the world would be able
to login on your Grafana instance.
You can can however limit access to only members of a given group or list of
groups by setting the `allowed_groups` option.
### allowed_groups
To limit access to authenticated users that are members of one or more [GitLab
groups](https://docs.gitlab.com/ce/user/group/index.html), set `allowed_groups`
to a comma- or space-separated list of groups. For instance, if you want to
only give access to members of the `example` group, set
```ini
allowed_groups = example
```
If you want to also give access to members of the subgroup `bar`, which is in
the group `foo`, set
```ini
allowed_groups = example, foo/bar
```
Note that in GitLab, the group or subgroup name doesn't always match its
display name, especially if the display name contains spaces or special
characters. Make sure you always use the group or subgroup name as it appears
in the URL of the group or subgroup.
Here's a complete example with `alloed_sign_up` enabled, and access limited to
the `example` and `foo/bar` groups:
```ini
[auth.gitlab]
enabled = false
allow_sign_up = true
client_id = GITLAB_APPLICATION_ID
client_secret = GITLAB_SECRET
scopes = api
auth_url = https://gitlab.com/oauth/authorize
token_url = https://gitlab.com/oauth/token
api_url = https://gitlab.com/api/v4
allowed_groups = example, foo/bar
```
<hr>
## [auth.google]
First, you need to create a Google OAuth Client:
@ -689,9 +799,9 @@ session provider you have configured.
- **file:** session file path, e.g. `data/sessions`
- **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
- **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=verify-full
- **memcache:** ex: 127.0.0.1:11211
- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,prefix=grafana`
- **postgres:** ex: `user=a password=b host=localhost port=5432 dbname=c sslmode=verify-full`
- **memcache:** ex: `127.0.0.1:11211`
- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,prefix=grafana`. For unix socket, use for example: `network=unix,addr=/var/run/redis/redis.sock,pool_size=100,db=grafana`
Postgres valid `sslmode` are `disable`, `require`, `verify-ca`, and `verify-full` (default).
@ -857,7 +967,7 @@ Secret key. e.g. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Url to where Grafana will send PUT request with images
### public_url
Optional parameter. Url to send to users in notifications, directly appended with the resulting uploaded file name.
Optional parameter. Url to send to users in notifications. If the string contains the sequence ${file}, it will be replaced with the uploaded filename. Otherwise, the file name will be appended to the path part of the url, leaving any query string unchanged.
### username
basic auth username

View File

@ -38,6 +38,8 @@ The back-end web server has a number of configuration options. Go to the
[Configuration]({{< relref "configuration.md" >}}) page for details on all
those options.
> For any changes to `conf/grafana.ini` (or corresponding environment variables) to take effect you need to restart Grafana by restarting the Docker container.
## Running a Specific Version of Grafana
```bash
@ -49,10 +51,13 @@ $ docker run \
grafana/grafana:5.1.0
```
## Running of the master branch
## Running the master branch
For every successful commit we publish a Grafana container to [`grafana/grafana`](https://hub.docker.com/r/grafana/grafana/tags/) and [`grafana/grafana-dev`](https://hub.docker.com/r/grafana/grafana-dev/tags/). In `grafana/grafana` container we will always overwrite the `master` tag with the latest version. In `grafana/grafana-dev` we will include
the git commit in the tag. If you run Grafana master in production we **strongly** recommend that you use the later since different machines might run different version of grafana if they pull the master tag at different times.
For every successful build of the master branch we update the `grafana/grafana:master` tag and create a new tag `grafana/grafana-dev:master-<commit hash>` with the hash of the git commit that was built. This means you can always get the latest version of Grafana.
When running Grafana master in production we **strongly** recommend that you use the `grafana/grafana-dev:master-<commit hash>` tag as that will guarantee that you use a specific version of Grafana instead of whatever was the most recent commit at the time.
For a list of available tags, check out [grafana/grafana](https://hub.docker.com/r/grafana/grafana/tags/) and [grafana/grafana-dev](https://hub.docker.com/r/grafana/grafana-dev/tags/).
## Installing Plugins for Grafana

View File

@ -23,8 +23,9 @@ specific configuration file (default: `/etc/grafana/ldap.toml`).
### Example config
```toml
# Set to true to log user information returned from LDAP
verbose_logging = false
# To troubleshoot and get more log info enable ldap debug logging in grafana.ini
# [log]
# filters = ldap:debug
[[servers]]
# Ldap server host (specify multiple hosts space separated)
@ -39,6 +40,9 @@ start_tls = false
ssl_skip_verify = false
# set to the path to your root CA certificate or leave unset to use system defaults
# root_ca_cert = "/path/to/certificate.crt"
# Authentication against LDAP servers requiring client certificates
# client_cert = "/path/to/client.crt"
# client_key = "/path/to/client.key"
# Search user bind dn
bind_dn = "cn=admin,dc=grafana,dc=org"
@ -47,6 +51,7 @@ bind_dn = "cn=admin,dc=grafana,dc=org"
bind_password = 'grafana'
# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"
# Allow login from email or username, example "(|(sAMAccountName=%s)(userPrincipalName=%s))"
search_filter = "(cn=%s)"
# An array of base dns to search through
@ -73,6 +78,8 @@ email = "email"
[[servers.group_mappings]]
group_dn = "cn=admins,dc=grafana,dc=org"
org_role = "Admin"
# To make user an instance admin (Grafana Admin) uncomment line below
# grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used. Setting this allows for multiple group_dn's to be assigned to the same org_role provided the org_id differs
# org_id = 1
@ -132,6 +139,10 @@ Users page, this change will be reset the next time the user logs in. If you
change the LDAP groups of a user, the change will take effect the next
time the user logs in.
### Grafana Admin
with a servers.group_mappings section you can set grafana_admin = true or false to sync Grafana Admin permission. A Grafana server admin has admin access over all orgs &
users.
### Priority
The first group mapping that an LDAP user is matched to will be used for the sync. If you have LDAP users that fit multiple mappings, the topmost mapping in the TOML config will be used.

View File

@ -57,7 +57,7 @@ For this you need nodejs (v.6+).
```bash
npm install -g yarn
yarn install --pure-lockfile
npm run watch
yarn watch
```
## Running Grafana Locally
@ -83,21 +83,18 @@ go get github.com/Unknwon/bra
bra run
```
You'll also need to run `npm run watch` to watch for changes to the front-end (typescript, html, sass)
You'll also need to run `yarn watch` to watch for changes to the front-end (typescript, html, sass)
### Running tests
- You can run backend Golang tests using "go test ./pkg/...".
- Execute all frontend tests with "npm run test"
- You can run backend Golang tests using `go test ./pkg/...`.
- Execute all frontend tests with `yarn test`
Writing & watching frontend tests (we have two test runners)
Writing & watching frontend tests
- Start watcher: `yarn jest`
- Jest will run all test files that end with the name ".test.ts"
- jest for all new tests that do not require browser context (React+more)
- Start watcher: `npm run jest`
- Jest will run all test files that end with the name ".jest.ts"
- karma + mocha is used for testing angularjs components. We do want to migrate these test to jest over time (if possible).
- Start watcher: `npm run karma`
- Karma+Mocha runs all files that end with the name "_specs.ts".
## Creating optimized release packages

View File

@ -11,7 +11,7 @@ weight = 1
# Variables
Variables allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application
and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of
and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of
the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
{{< docs-imagebox img="/img/docs/v50/variables_dashboard.png" >}}
@ -273,29 +273,49 @@ The `$__timeFilter` is used in the MySQL data source.
This variable is only available in the Singlestat panel and can be used in the prefix or suffix fields on the Options tab. The variable will be replaced with the series name or alias.
### The $__range Variable
> Only available in Grafana v5.3+
Currently only supported for Prometheus data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
## Repeating Panels
Template variables can be very useful to dynamically change your queries across a whole dashboard. If you want
Grafana to dynamically create new panels or rows based on what values you have selected you can use the *Repeat* feature.
If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel or one row and have Grafana repeat that row
for every selected value. You find this option under the General tab in panel edit mode. Select the variable to repeat by, and a `min span`.
The `min span` controls how small Grafana will make the panels (if you have many values selected). Grafana will automatically adjust the width of
each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated panel.
If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel and have Grafana repeat that panel
for every selected value. You find the *Repeat* feature under the *General tab* in panel edit mode.
The `direction` controls how the panels will be arranged.
By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width
of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated
panel. Each panel will never be smaller that the provided `Min width` if you have many selected values.
By choosing `vertical` the panels will be arranged from top to bottom in a column. The `Min width` doesn't have any effect in this case. The width of the repeated panels will be the same as of the first panel (the original template) being repeated.
Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build.
You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard.
## Repeating Rows
This option requires you to open the row options view. Hover over the row left side to trigger the row menu, in this menu click `Row Options`. This
opens the row options view. Here you find a *Repeat* dropdown where you can select the variable to repeat by.
As seen above with the *Panels* you can also repeat *Rows* if you have variables set with `Multi-value` or
`Include all value` selection option.
### URL state
To enable this feature you need to first add a new *Row* using the *Add Panel* menu. Then by hovering the row title and
clicking on the cog button, you will access the `Row Options` configuration panel. You can then select the variable
you want to repeat the row for.
It may be a good idea to use a variable in the row title as well.
Example: [Repeated Rows Dashboard](http://play.grafana.org/dashboard/db/repeated-rows)
## URL state
Variable values are always synced to the URL using the syntax `var-<varname>=value`.
### Examples
## Examples
- [Graphite Templated Dashboard](http://play.grafana.org/dashboard/db/graphite-templated-nested)
- [Elasticsearch Templated Dashboard](http://play.grafana.org/dashboard/db/elasticsearch-templated)

View File

@ -13,7 +13,7 @@ module.exports = {
"roots": [
"<rootDir>/public"
],
"testRegex": "(\\.|/)(jest)\\.(jsx?|tsx?)$",
"testRegex": "(\\.|/)(test)\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",

View File

@ -1,40 +0,0 @@
var webpack = require('webpack');
var path = require('path');
var webpackTestConfig = require('./scripts/webpack/webpack.test.js');
module.exports = function(config) {
'use strict';
config.set({
frameworks: ['mocha', 'expect', 'sinon'],
// list of files / patterns to load in the browser
files: [
{ pattern: 'public/test/index.ts', watched: false }
],
preprocessors: {
'public/test/index.ts': ['webpack', 'sourcemap'],
},
webpack: webpackTestConfig,
webpackMiddleware: {
stats: 'minimal',
},
// list of files to exclude
exclude: [],
reporters: ['dots'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
captureTimeout: 20000,
singleRun: true,
// autoWatchBatchDelay: 1000,
// browserNoActivityTimeout: 60000,
});
};

View File

@ -32,9 +32,8 @@
"es6-shim": "^0.35.3",
"expect.js": "~0.2.0",
"expose-loader": "^0.7.3",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
"fork-ts-checker-webpack-plugin": "^0.4.1",
"fork-ts-checker-webpack-plugin": "^0.4.2",
"gaze": "^1.1.2",
"glob": "~7.0.0",
"grunt": "1.0.1",
@ -45,10 +44,7 @@
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-cssmin": "~1.0.2",
"grunt-contrib-jshint": "~1.1.0",
"grunt-exec": "^1.0.1",
"grunt-jscs": "3.0.1",
"grunt-karma": "~2.0.0",
"grunt-notify": "^0.4.5",
"grunt-postcss": "^0.8.0",
"grunt-sass": "^2.0.0",
@ -60,23 +56,16 @@
"html-webpack-plugin": "^3.2.0",
"husky": "^0.14.3",
"jest": "^22.0.4",
"jshint-stylish": "~2.2.1",
"karma": "1.7.0",
"karma-chrome-launcher": "~2.2.0",
"karma-expect": "~1.1.3",
"karma-mocha": "~1.3.0",
"karma-phantomjs-launcher": "1.0.4",
"karma-sinon": "^1.0.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.0",
"lint-staged": "^6.0.0",
"load-grunt-tasks": "3.5.2",
"mini-css-extract-plugin": "^0.4.0",
"mobx-react-devtools": "^4.2.15",
"mocha": "^4.0.1",
"ng-annotate-loader": "^0.6.1",
"ng-annotate-webpack-plugin": "^0.2.1-pre",
"ng-annotate-webpack-plugin": "^0.3.0",
"ngtemplate-loader": "^2.0.1",
"npm": "^5.4.2",
"optimize-css-assets-webpack-plugin": "^4.0.2",
"phantomjs-prebuilt": "^2.1.15",
"postcss-browser-reporter": "^0.5.0",
"postcss-loader": "^2.0.6",
@ -90,15 +79,16 @@
"style-loader": "^0.21.0",
"systemjs": "0.20.19",
"systemjs-plugin-css": "^0.1.36",
"ts-loader": "^4.3.0",
"ts-jest": "^22.4.6",
"ts-loader": "^4.3.0",
"tslib": "^1.9.3",
"tslint": "^5.8.0",
"tslint-loader": "^3.5.3",
"typescript": "^2.6.2",
"uglifyjs-webpack-plugin": "^1.2.7",
"webpack": "^4.8.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-cleanup-plugin": "^0.5.1",
"fork-ts-checker-webpack-plugin": "^0.4.2",
"webpack-cli": "^2.1.4",
"webpack-dev-server": "^3.1.0",
"webpack-merge": "^4.1.0",
@ -112,7 +102,6 @@
"test": "grunt test",
"test:coverage": "grunt test --coverage=true",
"lint": "tslint -c tslint.json --project tsconfig.json --type-check",
"karma": "grunt karma:dev",
"jest": "jest --notify --watch",
"api-tests": "jest --notify --watch --config=tests/api/jest.js",
"precommit": "lint-staged && grunt precommit"
@ -155,16 +144,15 @@
"immutable": "^3.8.2",
"jquery": "^3.2.1",
"lodash": "^4.17.10",
"mini-css-extract-plugin": "^0.4.0",
"mobx": "^3.4.1",
"mobx-react": "^4.3.5",
"mobx-state-tree": "^1.3.1",
"moment": "^2.22.2",
"mousetrap": "^1.6.0",
"mousetrap-global-bind": "^1.1.0",
"optimize-css-assets-webpack-plugin": "^4.0.2",
"prismjs": "^1.6.0",
"prop-types": "^15.6.0",
"rc-cascader": "^0.14.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-grid-layout": "0.16.6",
@ -182,7 +170,7 @@
"tether": "^1.4.0",
"tether-drop": "https://github.com/torkelo/drop/tarball/master",
"tinycolor2": "^1.4.1",
"uglifyjs-webpack-plugin": "^1.2.7"
"tslint-react": "^3.6.0"
},
"resolutions": {
"caniuse-db": "1.0.30000772"

View File

@ -0,0 +1,52 @@
FROM debian:stretch-slim
ARG GRAFANA_TGZ="grafana-latest.linux-x64.tar.gz"
RUN apt-get update && apt-get install -qq -y tar && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz
RUN mkdir /tmp/grafana && tar xfvz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana
FROM debian:stretch-slim
ARG GF_UID="472"
ARG GF_GID="472"
ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \
GF_PATHS_DATA="/var/lib/grafana" \
GF_PATHS_HOME="/usr/share/grafana" \
GF_PATHS_LOGS="/var/log/grafana" \
GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \
GF_PATHS_PROVISIONING="/etc/grafana/provisioning"
WORKDIR $GF_PATHS_HOME
RUN apt-get update && apt-get install -qq -y libfontconfig ca-certificates && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
COPY --from=0 /tmp/grafana "$GF_PATHS_HOME"
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
groupadd -r -g $GF_GID grafana && \
useradd -r -u $GF_UID -g grafana grafana && \
mkdir -p "$GF_PATHS_PROVISIONING/datasources" \
"$GF_PATHS_PROVISIONING/dashboards" \
"$GF_PATHS_LOGS" \
"$GF_PATHS_PLUGINS" \
"$GF_PATHS_DATA" && \
cp "$GF_PATHS_HOME/conf/sample.ini" "$GF_PATHS_CONFIG" && \
cp "$GF_PATHS_HOME/conf/ldap.toml" /etc/grafana/ldap.toml && \
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" && \
chmod 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS"
EXPOSE 3000
COPY ./run.sh /run.sh
USER grafana
ENTRYPOINT [ "/run.sh" ]

View File

@ -0,0 +1,43 @@
# Grafana Docker image
## Running your Grafana container
Start your container binding the external port `3000`.
```bash
docker run -d --name=grafana -p 3000:3000 grafana/grafana
```
Try it out, default admin user is admin/admin.
## How to use the container
Further documentation can be found at http://docs.grafana.org/installation/docker/
## Changelog
### v5.1.5, v5.2.0-beta2
* Fix: config keys ending with _FILE are not respected [#170](https://github.com/grafana/grafana-docker/issues/170)
### v5.2.0-beta1
* Support for Docker Secrets
### v5.1.0
* Major restructuring of the container
* Usage of `chown` removed
* File permissions incompatibility with previous versions
* user id changed from 104 to 472
* group id changed from 107 to 472
* Runs as the grafana user by default (instead of root)
* All default volumes removed
### v4.2.0
* Plugins are now installed into ${GF_PATHS_PLUGINS}
* Building the container now requires a full url to the deb package instead of just version
* Fixes bug caused by installing multiple plugins
### v4.0.0-beta2
* Plugins dir (`/var/lib/grafana/plugins`) is no longer a separate volume
### v3.1.1
* Make it possible to install specific plugin version https://github.com/grafana/grafana-docker/issues/59#issuecomment-260584026

View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
_grafana_version=$1
./build.sh "$_grafana_version"
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
./push_to_docker_hub.sh "$_grafana_version"
if echo "$_grafana_version" | grep -q "^master-"; then
apk add --no-cache curl
./deploy_to_k8s.sh "grafana/grafana-dev:$_grafana_version"
fi

25
packaging/docker/build.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
_grafana_tag=$1
# If the tag starts with v, treat this as a official release
if echo "$_grafana_tag" | grep -q "^v"; then
_grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2)
_docker_repo=${2:-grafana/grafana}
else
_grafana_version=$_grafana_tag
_docker_repo=${2:-grafana/grafana-dev}
fi
echo "Building ${_docker_repo}:${_grafana_version}"
docker build \
--tag "${_docker_repo}:${_grafana_version}" \
--no-cache=true .
# Tag as 'latest' for official release; otherwise tag as grafana/grafana:master
if echo "$_grafana_tag" | grep -q "^v"; then
docker tag "${_docker_repo}:${_grafana_version}" "${_docker_repo}:latest"
else
docker tag "${_docker_repo}:${_grafana_version}" "grafana/grafana:master"
fi

View File

@ -0,0 +1,16 @@
ARG GRAFANA_VERSION="latest"
FROM grafana/grafana:${GRAFANA_VERSION}
USER grafana
ARG GF_INSTALL_PLUGINS=""
RUN if [ ! -z "${GF_INSTALL_PLUGINS}" ]; then \
OLDIFS=$IFS; \
IFS=','; \
for plugin in ${GF_INSTALL_PLUGINS}; do \
IFS=$OLDIFS; \
grafana-cli --pluginsDir "$GF_PATHS_PLUGINS" plugins install ${plugin}; \
done; \
fi

View File

@ -0,0 +1,6 @@
#!/bin/sh
curl -s --header "Content-Type: application/json" \
--data "{\"build_parameters\": {\"CIRCLE_JOB\": \"deploy\", \"IMAGE_NAMES\": \"$1\"}}" \
--request POST \
https://circleci.com/api/v1.1/project/github/raintank/deployment_tools/tree/master?circle-token=$CIRCLE_TOKEN

View File

@ -0,0 +1,24 @@
#!/bin/sh
set -e
_grafana_tag=$1
# If the tag starts with v, treat this as a official release
if echo "$_grafana_tag" | grep -q "^v"; then
_grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2)
_docker_repo=${2:-grafana/grafana}
else
_grafana_version=$_grafana_tag
_docker_repo=${2:-grafana/grafana-dev}
fi
echo "pushing ${_docker_repo}:${_grafana_version}"
docker push "${_docker_repo}:${_grafana_version}"
if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then
echo "pushing ${_docker_repo}:latest"
docker push "${_docker_repo}:latest"
elif echo "$_grafana_tag" | grep -q "master"; then
echo "pushing grafana/grafana:master"
docker push grafana/grafana:master
fi

88
packaging/docker/run.sh Executable file
View File

@ -0,0 +1,88 @@
#!/bin/bash -e
PERMISSIONS_OK=0
if [ ! -r "$GF_PATHS_CONFIG" ]; then
echo "GF_PATHS_CONFIG='$GF_PATHS_CONFIG' is not readable."
PERMISSIONS_OK=1
fi
if [ ! -w "$GF_PATHS_DATA" ]; then
echo "GF_PATHS_DATA='$GF_PATHS_DATA' is not writable."
PERMISSIONS_OK=1
fi
if [ ! -r "$GF_PATHS_HOME" ]; then
echo "GF_PATHS_HOME='$GF_PATHS_HOME' is not readable."
PERMISSIONS_OK=1
fi
if [ $PERMISSIONS_OK -eq 1 ]; then
echo "You may have issues with file permissions, more information here: http://docs.grafana.org/installation/docker/#migration-from-a-previous-version-of-the-docker-container-to-5-1-or-later"
fi
if [ ! -d "$GF_PATHS_PLUGINS" ]; then
mkdir "$GF_PATHS_PLUGINS"
fi
if [ ! -z ${GF_AWS_PROFILES+x} ]; then
> "$GF_PATHS_HOME/.aws/credentials"
for profile in ${GF_AWS_PROFILES}; do
access_key_varname="GF_AWS_${profile}_ACCESS_KEY_ID"
secret_key_varname="GF_AWS_${profile}_SECRET_ACCESS_KEY"
region_varname="GF_AWS_${profile}_REGION"
if [ ! -z "${!access_key_varname}" -a ! -z "${!secret_key_varname}" ]; then
echo "[${profile}]" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_access_key_id = ${!access_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_secret_access_key = ${!secret_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
if [ ! -z "${!region_varname}" ]; then
echo "region = ${!region_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
fi
fi
done
chmod 600 "$GF_PATHS_HOME/.aws/credentials"
fi
# Convert all environment variables with names ending in __FILE into the content of
# the file that they point at and use the name without the trailing __FILE.
# This can be used to carry in Docker secrets.
for VAR_NAME in $(env | grep '^GF_[^=]\+__FILE=.\+' | sed -r "s/([^=]*)__FILE=.*/\1/g"); do
VAR_NAME_FILE="$VAR_NAME"__FILE
if [ "${!VAR_NAME}" ]; then
echo >&2 "ERROR: Both $VAR_NAME and $VAR_NAME_FILE are set (but are exclusive)"
exit 1
fi
echo "Getting secret $VAR_NAME from ${!VAR_NAME_FILE}"
export "$VAR_NAME"="$(< "${!VAR_NAME_FILE}")"
unset "$VAR_NAME_FILE"
done
export HOME="$GF_PATHS_HOME"
if [ ! -z "${GF_INSTALL_PLUGINS}" ]; then
OLDIFS=$IFS
IFS=','
for plugin in ${GF_INSTALL_PLUGINS}; do
IFS=$OLDIFS
if [[ $plugin =~ .*\;.* ]]; then
pluginUrl=$(echo "$plugin" | cut -d';' -f 1)
pluginWithoutUrl=$(echo "$plugin" | cut -d';' -f 2)
grafana-cli --pluginUrl "${pluginUrl}" --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${pluginWithoutUrl}
else
grafana-cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin}
fi
done
fi
exec grafana-server \
--homepath="$GF_PATHS_HOME" \
--config="$GF_PATHS_CONFIG" \
"$@" \
cfg:default.log.mode="console" \
cfg:default.paths.data="$GF_PATHS_DATA" \
cfg:default.paths.logs="$GF_PATHS_LOGS" \
cfg:default.paths.plugins="$GF_PATHS_PLUGINS" \
cfg:default.paths.provisioning="$GF_PATHS_PROVISIONING"

View File

@ -73,8 +73,7 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/dashboards/", reqSignedIn, Index)
r.Get("/dashboards/*", reqSignedIn, Index)
r.Get("/explore/", reqEditorRole, Index)
r.Get("/explore/*", reqEditorRole, Index)
r.Get("/explore", reqEditorRole, Index)
r.Get("/playlists/", reqSignedIn, Index)
r.Get("/playlists/*", reqSignedIn, Index)
@ -121,6 +120,7 @@ func (hs *HTTPServer) registerRoutes() {
userRoute.Put("/", bind(m.UpdateUserCommand{}), Wrap(UpdateSignedInUser))
userRoute.Post("/using/:id", Wrap(UserSetUsingOrg))
userRoute.Get("/orgs", Wrap(GetSignedInUserOrgList))
userRoute.Get("/teams", Wrap(GetSignedInUserTeamList))
userRoute.Post("/stars/dashboard/:id", Wrap(StarDashboard))
userRoute.Delete("/stars/dashboard/:id", Wrap(UnstarDashboard))

View File

@ -158,12 +158,26 @@ func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
}
return Error(500, "Failed to update datasource", err)
}
ds := convertModelToDtos(cmd.Result)
query := m.GetDataSourceByIdQuery{
Id: cmd.Id,
OrgId: c.OrgId,
}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrDataSourceNotFound {
return Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
}
dtos := convertModelToDtos(query.Result)
return JSON(200, util.DynMap{
"message": "Datasource updated",
"id": cmd.Id,
"name": cmd.Name,
"datasource": ds,
"datasource": dtos,
})
}

View File

@ -78,7 +78,13 @@ func tryLoginUsingRememberCookie(c *m.ReqContext) bool {
user := userQuery.Result
// validate remember me cookie
if val, _ := c.GetSuperSecureCookie(user.Rands+user.Password, setting.CookieRememberName); val != user.Login {
signingKey := user.Rands + user.Password
if len(signingKey) < 10 {
c.Logger.Error("Invalid user signingKey")
return false
}
if val, _ := c.GetSuperSecureCookie(signingKey, setting.CookieRememberName); val != user.Login {
return false
}

View File

@ -52,7 +52,7 @@ func QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) Response {
if res.Error != nil {
res.ErrorString = res.Error.Error()
resp.Message = res.ErrorString
statusCode = 500
statusCode = 400
}
}
@ -99,7 +99,7 @@ func GetTestDataRandomWalk(c *m.ReqContext) Response {
timeRange := tsdb.NewTimeRange(from, to)
request := &tsdb.TsdbQuery{TimeRange: timeRange}
dsInfo := &m.DataSource{Type: "grafana-testdata-datasource"}
dsInfo := &m.DataSource{Type: "testdata"}
request.Queries = append(request.Queries, &tsdb.Query{
RefId: "A",
IntervalMs: intervalMs,

View File

@ -160,6 +160,7 @@ func CreatePlaylist(c *m.ReqContext, cmd m.CreatePlaylistCommand) Response {
func UpdatePlaylist(c *m.ReqContext, cmd m.UpdatePlaylistCommand) Response {
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to save playlist", err)

View File

@ -203,6 +203,7 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) {
req.Header.Del("X-Forwarded-Host")
req.Header.Del("X-Forwarded-Port")
req.Header.Del("X-Forwarded-Proto")
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
// set X-Forwarded-For header
if req.RemoteAddr != "" {
@ -319,9 +320,15 @@ func (proxy *DataSourceProxy) applyRoute(req *http.Request) {
SecureJsonData: proxy.ds.SecureJsonData.Decrypt(),
}
routeURL, err := url.Parse(proxy.route.Url)
interpolatedURL, err := interpolateString(proxy.route.Url, data)
if err != nil {
logger.Error("Error parsing plugin route url")
logger.Error("Error interpolating proxy url", "error", err)
return
}
routeURL, err := url.Parse(interpolatedURL)
if err != nil {
logger.Error("Error parsing plugin route url", "error", err)
return
}

View File

@ -49,6 +49,13 @@ func TestDSRouteRule(t *testing.T) {
{Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
},
},
{
Path: "api/common",
Url: "{{.JsonData.dynamicUrl}}",
Headers: []plugins.AppPluginRouteHeader{
{Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
},
},
},
}
@ -57,7 +64,8 @@ func TestDSRouteRule(t *testing.T) {
ds := &m.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"clientId": "asd",
"clientId": "asd",
"dynamicUrl": "https://dynamic.grafana.com",
}),
SecureJsonData: map[string][]byte{
"key": key,
@ -83,6 +91,17 @@ func TestDSRouteRule(t *testing.T) {
})
})
Convey("When matching route path and has dynamic url", func() {
proxy := NewDataSourceProxy(ds, plugin, ctx, "api/common/some/method")
proxy.route = plugin.Routes[3]
proxy.applyRoute(req)
Convey("should add headers and interpolate the url", func() {
So(req.URL.String(), ShouldEqual, "https://dynamic.grafana.com/some/method")
So(req.Header.Get("x-header"), ShouldEqual, "my secret 123")
})
})
Convey("Validating request", func() {
Convey("plugin route with valid role", func() {
proxy := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method")
@ -212,20 +231,21 @@ func TestDSRouteRule(t *testing.T) {
})
Convey("When proxying graphite", func() {
setting.BuildVersion = "5.3.0"
plugin := &plugins.DataSourcePlugin{}
ds := &m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "/render")
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
requestURL, _ := url.Parse("http://grafana.com/sub")
req := http.Request{URL: requestURL}
proxy.getDirector()(&req)
proxy.getDirector()(req)
Convey("Can translate request url and path", func() {
So(req.URL.Host, ShouldEqual, "graphite:8080")
So(req.URL.Path, ShouldEqual, "/render")
So(req.Header.Get("User-Agent"), ShouldEqual, "Grafana/5.3.0")
})
})
@ -243,10 +263,10 @@ func TestDSRouteRule(t *testing.T) {
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "")
requestURL, _ := url.Parse("http://grafana.com/sub")
req := http.Request{URL: requestURL}
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(&req)
proxy.getDirector()(req)
Convey("Should add db to url", func() {
So(req.URL.Path, ShouldEqual, "/db/site/")

View File

@ -111,6 +111,21 @@ func GetSignedInUserOrgList(c *m.ReqContext) Response {
return getUserOrgList(c.UserId)
}
// GET /api/user/teams
func GetSignedInUserTeamList(c *m.ReqContext) Response {
query := m.GetTeamsByUserQuery{OrgId: c.OrgId, UserId: c.UserId}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get user teams", err)
}
for _, team := range query.Result {
team.AvatarUrl = dtos.GetGravatarUrlWithDefault(team.Email, team.Name)
}
return JSON(200, query.Result)
}
// GET /api/user/:id/orgs
func GetUserOrgList(c *m.ReqContext) Response {
return getUserOrgList(c.ParamsInt64(":id"))

View File

@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/grafana/grafana/pkg/util"
@ -35,6 +36,16 @@ var netClient = &http.Client{
Transport: netTransport,
}
func (u *WebdavUploader) PublicURL(filename string) string {
if strings.Contains(u.public_url, "${file}") {
return strings.Replace(u.public_url, "${file}", filename, -1)
} else {
publicURL, _ := url.Parse(u.public_url)
publicURL.Path = path.Join(publicURL.Path, filename)
return publicURL.String()
}
}
func (u *WebdavUploader) Upload(ctx context.Context, pa string) (string, error) {
url, _ := url.Parse(u.url)
filename := util.GetRandomString(20) + ".png"
@ -65,9 +76,7 @@ func (u *WebdavUploader) Upload(ctx context.Context, pa string) (string, error)
}
if u.public_url != "" {
publicURL, _ := url.Parse(u.public_url)
publicURL.Path = path.Join(publicURL.Path, filename)
return publicURL.String(), nil
return u.PublicURL(filename), nil
}
return url.String(), nil

View File

@ -2,6 +2,7 @@ package imguploader
import (
"context"
"net/url"
"testing"
. "github.com/smartystreets/goconvey/convey"
@ -26,3 +27,15 @@ func TestUploadToWebdav(t *testing.T) {
So(path, ShouldStartWith, "http://publicurl:8888/webdav/")
})
}
func TestPublicURL(t *testing.T) {
Convey("Given a public URL with parameters, and no template", t, func() {
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=")
parsed, _ := url.Parse(webdavUploader.PublicURL("fileyfile.png"))
So(parsed.Path, ShouldEndWith, "fileyfile.png")
})
Convey("Given a public URL with parameters, and a template", t, func() {
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=${file}")
So(webdavUploader.PublicURL("fileyfile.png"), ShouldEndWith, "fileyfile.png")
})
}

View File

@ -72,6 +72,13 @@ func UpsertUser(cmd *m.UpsertUserCommand) error {
return err
}
// Sync isGrafanaAdmin permission
if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin {
if err := bus.Dispatch(&m.UpdateUserPermissionsCommand{UserId: cmd.Result.Id, IsGrafanaAdmin: *extUser.IsGrafanaAdmin}); err != nil {
return err
}
}
err = bus.Dispatch(&m.SyncTeamsCommand{
User: cmd.Result,
ExternalUser: extUser,

View File

@ -59,6 +59,13 @@ func (a *ldapAuther) Dial() error {
}
}
}
var clientCert tls.Certificate
if a.server.ClientCert != "" && a.server.ClientKey != "" {
clientCert, err = tls.LoadX509KeyPair(a.server.ClientCert, a.server.ClientKey)
if err != nil {
return err
}
}
for _, host := range strings.Split(a.server.Host, " ") {
address := fmt.Sprintf("%s:%d", host, a.server.Port)
if a.server.UseSSL {
@ -67,6 +74,9 @@ func (a *ldapAuther) Dial() error {
ServerName: host,
RootCAs: certPool,
}
if len(clientCert.Certificate) > 0 {
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
}
if a.server.StartTLS {
a.conn, err = ldap.Dial("tcp", address)
if err == nil {
@ -175,6 +185,7 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo
if ldapUser.isMemberOf(group.GroupDN) {
extUser.OrgRoles[group.OrgId] = group.OrgRole
extUser.IsGrafanaAdmin = group.IsGrafanaAdmin
}
}
@ -190,18 +201,18 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo
}
// add/update user in grafana
userQuery := &m.UpsertUserCommand{
upsertUserCmd := &m.UpsertUserCommand{
ReqContext: ctx,
ExternalUser: extUser,
SignupAllowed: setting.LdapAllowSignup,
}
err := bus.Dispatch(userQuery)
err := bus.Dispatch(upsertUserCmd)
if err != nil {
return nil, err
}
return userQuery.Result, nil
return upsertUserCmd.Result, nil
}
func (a *ldapAuther) serverBind() error {

View File

@ -21,6 +21,8 @@ type LdapServerConf struct {
StartTLS bool `toml:"start_tls"`
SkipVerifySSL bool `toml:"ssl_skip_verify"`
RootCACert string `toml:"root_ca_cert"`
ClientCert string `toml:"client_cert"`
ClientKey string `toml:"client_key"`
BindDN string `toml:"bind_dn"`
BindPassword string `toml:"bind_password"`
Attr LdapAttributeMap `toml:"attributes"`
@ -44,9 +46,10 @@ type LdapAttributeMap struct {
}
type LdapGroupToOrgRole struct {
GroupDN string `toml:"group_dn"`
OrgId int64 `toml:"org_id"`
OrgRole m.RoleType `toml:"org_role"`
GroupDN string `toml:"group_dn"`
OrgId int64 `toml:"org_id"`
IsGrafanaAdmin *bool `toml:"grafana_admin"` // This is a pointer to know if it was set or not (for backwards compatability)
OrgRole m.RoleType `toml:"org_role"`
}
var LdapCfg LdapConfig

View File

@ -98,6 +98,10 @@ func TestLdapAuther(t *testing.T) {
So(result.Login, ShouldEqual, "torkelo")
})
Convey("Should set isGrafanaAdmin to false by default", func() {
So(result.IsAdmin, ShouldBeFalse)
})
})
})
@ -223,8 +227,32 @@ func TestLdapAuther(t *testing.T) {
So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
})
Convey("Should not update permissions unless specified", func() {
So(err, ShouldBeNil)
So(sc.updateUserPermissionsCmd, ShouldBeNil)
})
})
ldapAutherScenario("given ldap groups with grafana_admin=true", func(sc *scenarioContext) {
trueVal := true
ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin", IsGrafanaAdmin: &trueVal},
},
})
sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
_, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
MemberOf: []string{"cn=admins"},
})
Convey("Should create user with admin set to true", func() {
So(err, ShouldBeNil)
So(sc.updateUserPermissionsCmd.IsGrafanaAdmin, ShouldBeTrue)
})
})
})
Convey("When calling SyncUser", t, func() {
@ -332,6 +360,11 @@ func ldapAutherScenario(desc string, fn scenarioFunc) {
return nil
})
bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpdateUserPermissionsCommand) error {
sc.updateUserPermissionsCmd = cmd
return nil
})
bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
sc.getUserByAuthInfoQuery = cmd
sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login}
@ -379,14 +412,15 @@ func ldapAutherScenario(desc string, fn scenarioFunc) {
}
type scenarioContext struct {
getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
getUserOrgListQuery *m.GetUserOrgListQuery
createUserCmd *m.CreateUserCommand
addOrgUserCmd *m.AddOrgUserCommand
updateOrgUserCmd *m.UpdateOrgUserCommand
removeOrgUserCmd *m.RemoveOrgUserCommand
updateUserCmd *m.UpdateUserCommand
setUsingOrgCmd *m.SetUsingOrgCommand
getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
getUserOrgListQuery *m.GetUserOrgListQuery
createUserCmd *m.CreateUserCommand
addOrgUserCmd *m.AddOrgUserCommand
updateOrgUserCmd *m.UpdateOrgUserCommand
removeOrgUserCmd *m.RemoveOrgUserCommand
updateUserCmd *m.UpdateUserCommand
setUsingOrgCmd *m.SetUsingOrgCommand
updateUserPermissionsCmd *m.UpdateUserPermissionsCommand
}
func (sc *scenarioContext) userQueryReturns(user *m.User) {

View File

@ -44,6 +44,7 @@ var (
M_Alerting_Notification_Sent *prometheus.CounterVec
M_Aws_CloudWatch_GetMetricStatistics prometheus.Counter
M_Aws_CloudWatch_ListMetrics prometheus.Counter
M_Aws_CloudWatch_GetMetricData prometheus.Counter
M_DB_DataSource_QueryById prometheus.Counter
// Timers
@ -218,6 +219,12 @@ func init() {
Namespace: exporterName,
})
M_Aws_CloudWatch_GetMetricData = prometheus.NewCounter(prometheus.CounterOpts{
Name: "aws_cloudwatch_get_metric_data_total",
Help: "counter for getting metric data time series from aws",
Namespace: exporterName,
})
M_DB_DataSource_QueryById = prometheus.NewCounter(prometheus.CounterOpts{
Name: "db_datasource_query_by_id_total",
Help: "counter for getting datasource by id",
@ -307,6 +314,7 @@ func initMetricVars() {
M_Alerting_Notification_Sent,
M_Aws_CloudWatch_GetMetricStatistics,
M_Aws_CloudWatch_ListMetrics,
M_Aws_CloudWatch_GetMetricData,
M_DB_DataSource_QueryById,
M_Alerting_Active_Alerts,
M_StatTotal_Dashboards,

View File

@ -8,4 +8,5 @@ const (
TWITTER
GENERIC
GRAFANA_COM
GITLAB
)

View File

@ -63,7 +63,7 @@ type PlaylistDashboards []*PlaylistDashboard
type UpdatePlaylistCommand struct {
OrgId int64 `json:"-"`
Id int64 `json:"id" binding:"Required"`
Id int64 `json:"id"`
Name string `json:"name" binding:"Required"`
Interval string `json:"interval"`
Items []PlaylistItemDTO `json:"items"`

View File

@ -13,14 +13,15 @@ type UserAuth struct {
}
type ExternalUserInfo struct {
AuthModule string
AuthId string
UserId int64
Email string
Login string
Name string
Groups []string
OrgRoles map[int64]RoleType
AuthModule string
AuthId string
UserId int64
Email string
Login string
Name string
Groups []string
OrgRoles map[int64]RoleType
IsGrafanaAdmin *bool // This is a pointer to know if we should sync this or not (nil = ignore sync)
}
// ---------------------

View File

@ -17,11 +17,14 @@ import (
plugin "github.com/hashicorp/go-plugin"
)
// DataSourcePlugin contains all metadata about a datasource plugin
type DataSourcePlugin struct {
FrontendPluginBase
Annotations bool `json:"annotations"`
Metrics bool `json:"metrics"`
Alerting bool `json:"alerting"`
Explore bool `json:"explore"`
Logs bool `json:"logs"`
QueryOptions map[string]bool `json:"queryOptions,omitempty"`
BuiltIn bool `json:"builtIn,omitempty"`
Mixed bool `json:"mixed,omitempty"`

View File

@ -3,7 +3,6 @@ package alerting
import (
"errors"
"fmt"
"time"
"golang.org/x/sync/errgroup"
@ -81,7 +80,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
renderOpts := rendering.Opts{
Width: 1000,
Height: 500,
Timeout: time.Second * 30,
Timeout: alertTimeout / 2,
OrgId: context.Rule.OrgId,
OrgRole: m.ROLE_ADMIN,
}

View File

@ -58,7 +58,7 @@ func init() {
data-placement="right">
</input>
<info-popover mode="right-absolute">
Provide a bot token to use the Slack file.upload API (starts with "xoxb")
Provide a bot token to use the Slack file.upload API (starts with "xoxb"). Specify #channel-name or @username in Recipient for this to work
</info-popover>
</div>
`,

View File

@ -83,7 +83,7 @@ func (cr *configReader) parseDatasourceConfig(path string, file os.FileInfo) (*D
}
func validateDefaultUniqueness(datasources []*DatasourcesAsConfig) error {
defaultCount := 0
defaultCount := map[int64]int{}
for i := range datasources {
if datasources[i].Datasources == nil {
continue
@ -95,8 +95,8 @@ func validateDefaultUniqueness(datasources []*DatasourcesAsConfig) error {
}
if ds.IsDefault {
defaultCount++
if defaultCount > 1 {
defaultCount[ds.OrgId] = defaultCount[ds.OrgId] + 1
if defaultCount[ds.OrgId] > 1 {
return ErrInvalidConfigToManyDefault
}
}

View File

@ -19,6 +19,7 @@ var (
allProperties = "testdata/all-properties"
versionZero = "testdata/version-0"
brokenYaml = "testdata/broken-yaml"
multipleOrgsWithDefault = "testdata/multiple-org-default"
fakeRepo *fakeRepository
)
@ -73,6 +74,19 @@ func TestDatasourceAsConfig(t *testing.T) {
})
})
Convey("Multiple datasources in different organizations with isDefault in each organization", func() {
dc := newDatasourceProvisioner(logger)
err := dc.applyChanges(multipleOrgsWithDefault)
Convey("should not raise error", func() {
So(err, ShouldBeNil)
So(len(fakeRepo.inserted), ShouldEqual, 4)
So(fakeRepo.inserted[0].IsDefault, ShouldBeTrue)
So(fakeRepo.inserted[0].OrgId, ShouldEqual, 1)
So(fakeRepo.inserted[2].IsDefault, ShouldBeTrue)
So(fakeRepo.inserted[2].OrgId, ShouldEqual, 2)
})
})
Convey("Two configured datasource and purge others ", func() {
Convey("two other datasources in database", func() {
fakeRepo.loadAll = []*models.DataSource{

View File

@ -11,7 +11,7 @@ import (
)
var (
ErrInvalidConfigToManyDefault = errors.New("datasource.yaml config is invalid. Only one datasource can be marked as default")
ErrInvalidConfigToManyDefault = errors.New("datasource.yaml config is invalid. Only one datasource per organization can be marked as default")
)
func Provision(configDirectory string) error {

View File

@ -0,0 +1,25 @@
apiVersion: 1
datasources:
- orgId: 1
name: prometheus
type: prometheus
isDefault: True
access: proxy
url: http://prometheus.example.com:9090
- name: Graphite
type: graphite
access: proxy
url: http://localhost:8080
- orgId: 2
name: prometheus
type: prometheus
isDefault: True
access: proxy
url: http://prometheus.example.com:9090
- orgId: 2
name: Graphite
type: graphite
access: proxy
url: http://localhost:8080

View File

@ -73,6 +73,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
alert.name,
alert.state,
alert.new_state_date,
alert.eval_data,
alert.eval_date,
alert.execution_error,
dashboard.uid as dashboard_uid,

View File

@ -13,7 +13,7 @@ func mockTimeNow() {
var timeSeed int64
timeNow = func() time.Time {
fakeNow := time.Unix(timeSeed, 0)
timeSeed += 1
timeSeed++
return fakeNow
}
}
@ -30,7 +30,7 @@ func TestAlertingDataAccess(t *testing.T) {
InitTestDB(t)
testDash := insertTestDashboard("dashboard with alerts", 1, 0, false, "alert")
evalData, _ := simplejson.NewJson([]byte(`{"test": "test"}`))
items := []*m.Alert{
{
PanelId: 1,
@ -40,6 +40,7 @@ func TestAlertingDataAccess(t *testing.T) {
Message: "Alerting message",
Settings: simplejson.New(),
Frequency: 1,
EvalData: evalData,
},
}
@ -104,8 +105,18 @@ func TestAlertingDataAccess(t *testing.T) {
alert := alertQuery.Result[0]
So(err2, ShouldBeNil)
So(alert.Id, ShouldBeGreaterThan, 0)
So(alert.DashboardId, ShouldEqual, testDash.Id)
So(alert.PanelId, ShouldEqual, 1)
So(alert.Name, ShouldEqual, "Alerting title")
So(alert.State, ShouldEqual, "pending")
So(alert.NewStateDate, ShouldNotBeNil)
So(alert.EvalData, ShouldNotBeNil)
So(alert.EvalData.Get("test").MustString(), ShouldEqual, "test")
So(alert.EvalDate, ShouldNotBeNil)
So(alert.ExecutionError, ShouldEqual, "")
So(alert.DashboardUid, ShouldNotBeNil)
So(alert.DashboardSlug, ShouldEqual, "dashboard-with-alerts")
})
Convey("Viewer cannot read alerts", func() {

View File

@ -181,7 +181,7 @@ func TestDashboardDataAccess(t *testing.T) {
So(err, ShouldBeNil)
So(query.Result.FolderId, ShouldEqual, 0)
So(query.Result.CreatedBy, ShouldEqual, savedDash.CreatedBy)
So(query.Result.Created, ShouldEqual, savedDash.Created.Truncate(time.Second))
So(query.Result.Created, ShouldHappenWithin, 3*time.Second, savedDash.Created)
So(query.Result.UpdatedBy, ShouldEqual, 100)
So(query.Result.Updated.IsZero(), ShouldBeFalse)
})
@ -387,6 +387,7 @@ func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isF
func createUser(name string, role string, isAdmin bool) m.User {
setting.AutoAssignOrg = true
setting.AutoAssignOrgId = 1
setting.AutoAssignOrgRole = role
currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}

View File

@ -1,6 +1,12 @@
package migrations
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
import (
"fmt"
"github.com/go-xorm/xorm"
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/util"
)
func addUserMigrations(mg *Migrator) {
userV1 := Table{
@ -107,4 +113,37 @@ func addUserMigrations(mg *Migrator) {
mg.AddMigration("Add last_seen_at column to user", NewAddColumnMigration(userV2, &Column{
Name: "last_seen_at", Type: DB_DateTime, Nullable: true,
}))
// Adds salt & rands for old users who used ldap or oauth
mg.AddMigration("Add missing user data", &AddMissingUserSaltAndRandsMigration{})
}
type AddMissingUserSaltAndRandsMigration struct {
MigrationBase
}
func (m *AddMissingUserSaltAndRandsMigration) Sql(dialect Dialect) string {
return "code migration"
}
type TempUserDTO struct {
Id int64
Login string
}
func (m *AddMissingUserSaltAndRandsMigration) Exec(sess *xorm.Session, mg *Migrator) error {
users := make([]*TempUserDTO, 0)
err := sess.Sql(fmt.Sprintf("SELECT id, login from %s WHERE rands = ''", mg.Dialect.Quote("user"))).Find(&users)
if err != nil {
return err
}
for _, user := range users {
_, err := sess.Exec("UPDATE "+mg.Dialect.Quote("user")+" SET salt = ?, rands = ? WHERE id = ?", util.GetRandomString(10), util.GetRandomString(10), user.Id)
if err != nil {
return err
}
}
return nil
}

View File

@ -12,7 +12,7 @@ import (
type Migrator struct {
x *xorm.Engine
dialect Dialect
Dialect Dialect
migrations []Migration
Logger log.Logger
}
@ -31,7 +31,7 @@ func NewMigrator(engine *xorm.Engine) *Migrator {
mg.x = engine
mg.Logger = log.New("migrator")
mg.migrations = make([]Migration, 0)
mg.dialect = NewDialect(mg.x)
mg.Dialect = NewDialect(mg.x)
return mg
}
@ -86,7 +86,7 @@ func (mg *Migrator) Start() error {
continue
}
sql := m.Sql(mg.dialect)
sql := m.Sql(mg.Dialect)
record := MigrationLog{
MigrationId: m.Id(),
@ -122,7 +122,7 @@ func (mg *Migrator) exec(m Migration, sess *xorm.Session) error {
condition := m.GetCondition()
if condition != nil {
sql, args := condition.Sql(mg.dialect)
sql, args := condition.Sql(mg.Dialect)
results, err := sess.SQL(sql).Query(args...)
if err != nil || len(results) == 0 {
mg.Logger.Debug("Skipping migration condition not fulfilled", "id", m.Id())
@ -130,7 +130,13 @@ func (mg *Migrator) exec(m Migration, sess *xorm.Session) error {
}
}
_, err := sess.Exec(m.Sql(mg.dialect))
var err error
if codeMigration, ok := m.(CodeMigration); ok {
err = codeMigration.Exec(sess, mg)
} else {
_, err = sess.Exec(m.Sql(mg.Dialect))
}
if err != nil {
mg.Logger.Error("Executing migration failed", "id", m.Id(), "error", err)
return err

View File

@ -3,6 +3,8 @@ package migrator
import (
"fmt"
"strings"
"github.com/go-xorm/xorm"
)
const (
@ -19,6 +21,11 @@ type Migration interface {
GetCondition() MigrationCondition
}
type CodeMigration interface {
Migration
Exec(sess *xorm.Session, migrator *Migrator) error
}
type SQLType string
type ColumnType string

Some files were not shown because too many files have changed in this diff Show More