Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64017e8ca6 | ||
|
|
f8239a2157 | ||
|
|
c8bd372532 | ||
|
|
164fb230cb | ||
|
|
7382b1be90 | ||
|
|
d38ac63f22 | ||
|
|
67f503b820 | ||
|
|
4ec37fb058 | ||
|
|
4fee0645a1 | ||
|
|
b03e28fc60 | ||
|
|
db55065687 | ||
|
|
7ca5c227dd | ||
|
|
3dc8434cf4 | ||
|
|
8cb58c1560 | ||
|
|
63f6207a1f | ||
|
|
97ee2cf918 | ||
|
|
b642a797fd | ||
|
|
9b07bc7e98 | ||
|
|
94594d61e6 | ||
|
|
0026764f04 | ||
|
|
a09a23c8bf | ||
|
|
e4af9fba96 | ||
|
|
f70d51c2c6 | ||
|
|
b8835eba17 | ||
|
|
4e2d4c42f0 | ||
|
|
ac66ba77da | ||
|
|
afab41a9c4 | ||
|
|
72cf8605c0 | ||
|
|
7ba1e9e258 | ||
|
|
f07841db8a | ||
|
|
797dd2dd58 | ||
|
|
ab3718afba | ||
|
|
d1df696c31 | ||
|
|
dfeb978e3b | ||
|
|
8b7664294c | ||
|
|
5895100beb | ||
|
|
844b08962d | ||
|
|
551c4aa562 | ||
|
|
1ac0c03a1d | ||
|
|
484b098a20 | ||
|
|
ca73c43b56 | ||
|
|
6c027ccf6a | ||
|
|
0275f11785 | ||
|
|
3274e8727e | ||
|
|
522a819e9e | ||
|
|
f6abf640eb | ||
|
|
47b635270e | ||
|
|
36bfb144b7 | ||
|
|
fade9fb6ae | ||
|
|
ad2ea44977 | ||
|
|
924180a25b | ||
|
|
76ed60064d | ||
|
|
e09307c078 | ||
|
|
ec9c2ed029 | ||
|
|
92b10cc965 | ||
|
|
3be1ddc56a | ||
|
|
dac165a7fc | ||
|
|
44655b96cb | ||
|
|
79104ef556 | ||
|
|
95189820fe | ||
|
|
68d7dadaec | ||
|
|
2bfc106473 | ||
|
|
82430165ee | ||
|
|
85f1aab363 | ||
|
|
3df51bf025 | ||
|
|
b1d914bceb | ||
|
|
e0e74ea6d2 | ||
|
|
b94c64ef48 | ||
|
|
43d3f21ff0 | ||
|
|
a62cb4e6c1 | ||
|
|
23b791c234 | ||
|
|
0b27b6a8c2 | ||
|
|
e71e6ae07e | ||
|
|
2576f05029 | ||
|
|
e8cff14164 | ||
|
|
a162fdf32f | ||
|
|
99f6606285 | ||
|
|
d2f721c18e | ||
|
|
efd139ecf3 | ||
|
|
5647a981ed | ||
|
|
d6f248c14e | ||
|
|
f58d6107e1 | ||
|
|
b3a46212df | ||
|
|
3289770198 | ||
|
|
8d7c3f19ee |
@@ -7416,8 +7416,7 @@ exports[`better eslint`] = {
|
||||
],
|
||||
"public/app/plugins/datasource/prometheus/querybuilder/state.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/prometheus/querybuilder/state.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
|
||||
559
.drone.yml
559
.drone.yml
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ on:
|
||||
|
||||
jobs:
|
||||
workflow-call:
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.12
|
||||
with:
|
||||
frontend-path-regexp: public\/app\/plugins\/datasource\/(grafana-azure-monitor-datasource|cloud-monitoring|cloudwatch)
|
||||
backend-path-regexp: pkg\/tsdb\/(azuremonitor|cloudmonitoring|cloudwatch)
|
||||
|
||||
13
.github/workflows/codeql-analysis.yml
vendored
13
.github/workflows/codeql-analysis.yml
vendored
@@ -6,6 +6,7 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, v1.8.x, v2.0.x, v2.1.x, v2.6.x, v3.0.x, v3.1.x, v4.0.x, v4.1.x, v4.2.x, v4.3.x, v4.4.x, v4.5.x, v4.6.x, v4.7.x, v5.0.x, v5.1.x, v5.2.x, v5.3.x, v5.4.x, v6.0.x, v6.1.x, v6.2.x, v6.3.x, v6.4.x, v6.5.x, v6.6.x, v6.7.x, v7.0.x, v7.1.x, v7.2.x]
|
||||
paths-ignore:
|
||||
@@ -39,6 +40,12 @@ jobs:
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
- if: matrix.language == 'go'
|
||||
name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.19.2'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
@@ -49,5 +56,11 @@ jobs:
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- if: matrix.language == 'go'
|
||||
name: Build go files
|
||||
run: |
|
||||
go mod verify
|
||||
make build-go
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
2
.github/workflows/ox-code-coverage.yml
vendored
2
.github/workflows/ox-code-coverage.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
|
||||
jobs:
|
||||
workflow-call:
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.12
|
||||
with:
|
||||
frontend-path-regexp: public\/app\/features\/(explore|correlations)
|
||||
backend-path-regexp: pkg\/services\/(queryhistory)
|
||||
|
||||
11
.github/workflows/pr-codeql-analysis-go.yml
vendored
11
.github/workflows/pr-codeql-analysis-go.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: "CodeQL for PR / go"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
@@ -19,11 +20,21 @@ jobs:
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.19.2'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: "go"
|
||||
|
||||
- name: Build go files
|
||||
run: |
|
||||
go mod verify
|
||||
make build-go
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: "CodeQL for PR / javascript"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: "CodeQL for PR / python"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
|
||||
@@ -62,7 +62,7 @@ var config = {
|
||||
url: '${HOST}/login',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 12,
|
||||
threshold: 13,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/login',
|
||||
@@ -74,7 +74,7 @@ var config = {
|
||||
"click element button[aria-label='Login button']",
|
||||
"wait for element [aria-label='Skip change password button'] to be visible",
|
||||
],
|
||||
threshold: 13,
|
||||
threshold: 14,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
@@ -101,49 +101,49 @@ var config = {
|
||||
rootElement: '.main-view',
|
||||
// the unified alerting promotion alert's content contrast is too low
|
||||
// see https://github.com/grafana/grafana/pull/41829
|
||||
threshold: 5,
|
||||
threshold: 6,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/datasources',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/users',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/teams',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/plugins',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/apikeys',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 3,
|
||||
threshold: 4,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/dashboards',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -60,9 +60,6 @@ packageExtensions:
|
||||
react-simple-compat: 1.2.2
|
||||
peerDependencies:
|
||||
framework-utils: ^1.1.0
|
||||
react-docgen-typescript-loader@3.7.2:
|
||||
peerDependencies:
|
||||
webpack: 4.41.5
|
||||
react-icons@2.2.7:
|
||||
peerDependencies:
|
||||
prop-types: "*"
|
||||
|
||||
80
CHANGELOG.md
80
CHANGELOG.md
@@ -1,3 +1,83 @@
|
||||
<!-- 9.2.3 START -->
|
||||
|
||||
# 9.2.3 (2022-10-31)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Docs:** Add information about DB version support to upgrade guide. [#57643](https://github.com/grafana/grafana/pull/57643), [@joeblubaugh](https://github.com/joeblubaugh)
|
||||
- **Footer:** Update footer release notes link to Github changelog. [#57871](https://github.com/grafana/grafana/pull/57871), [@joshhunt](https://github.com/joshhunt)
|
||||
- **Prometheus:** Do not drop errors in streaming parser. [#57698](https://github.com/grafana/grafana/pull/57698), [@kylebrandt](https://github.com/kylebrandt)
|
||||
- **Prometheus:** Flavor/version configuration. [#57554](https://github.com/grafana/grafana/pull/57554), [@gtk-grafana](https://github.com/gtk-grafana)
|
||||
- **Prometheus:** Provide label values match parameter API when supported prometheus instance is configured. [#57553](https://github.com/grafana/grafana/pull/57553), [@gtk-grafana](https://github.com/gtk-grafana)
|
||||
- **Security:** Upgrade x/text to version unaffected by CVE-2022-32149. [#57797](https://github.com/grafana/grafana/pull/57797), [@yong-jie-gong](https://github.com/yong-jie-gong)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Access control:** Fix a bug with argument order for data source managed permission updates. (Enterprise)
|
||||
- **Auth:** Fix GF_AUTH_JWT_URL_LOGIN env variable doesn't work. [#57689](https://github.com/grafana/grafana/pull/57689), [@Jguer](https://github.com/Jguer)
|
||||
- **Live:** Explicitly reply with http 200. [#57428](https://github.com/grafana/grafana/pull/57428), [@sh0rez](https://github.com/sh0rez)
|
||||
- **Prometheus:** Fix builder operation mode changing multiselect to single select behaviour. [#57780](https://github.com/grafana/grafana/pull/57780), [@itsmylife](https://github.com/itsmylife)
|
||||
- **Prometheus:** Fix builder operation mode changing multiselect to single select behaviour. [#57493](https://github.com/grafana/grafana/pull/57493), [@yinjialu](https://github.com/yinjialu)
|
||||
- **Security:** Fix vulnerabilities in webpack loader-utils. [#57533](https://github.com/grafana/grafana/pull/57533), [@jackw](https://github.com/jackw)
|
||||
|
||||
<!-- 9.2.3 END -->
|
||||
<!-- 9.2.2 START -->
|
||||
|
||||
# 9.2.2 (2022-10-25)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Add support for wecom apiapp. [#55991](https://github.com/grafana/grafana/pull/55991), [@aimuz](https://github.com/aimuz)
|
||||
- **Canvas:** Improve resource picker initialization. [#57319](https://github.com/grafana/grafana/pull/57319), [@nmarrs](https://github.com/nmarrs)
|
||||
- **Canvas:** Improve text element readability. [#57371](https://github.com/grafana/grafana/pull/57371), [@adela-almasan](https://github.com/adela-almasan)
|
||||
- **CloudWatch:** Make sure adoption tracking is done on valid, migrated queries. [#56872](https://github.com/grafana/grafana/pull/56872), [@sunker](https://github.com/sunker)
|
||||
- **Dashboard:** Alerts user to incorrect tag format for JSON import. [#54657](https://github.com/grafana/grafana/pull/54657), [@iamelDuderino](https://github.com/iamelDuderino)
|
||||
- **MSSQL:** Support tables from all schemas. [#53099](https://github.com/grafana/grafana/pull/53099), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **Opentsdb:** Allow template variables for filter keys. [#57226](https://github.com/grafana/grafana/pull/57226), [@bohandley](https://github.com/bohandley)
|
||||
- **Prometheus:** Provide label values match parameter API when supported prometheus instance is configured. [#56510](https://github.com/grafana/grafana/pull/56510), [@gtk-grafana](https://github.com/gtk-grafana)
|
||||
- **QueryEditor:** Revert components from grafana-ui. [#57436](https://github.com/grafana/grafana/pull/57436), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **TeamSync:** Allow team sync when external organization mapping returns no organization role. (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Browse:** Fix General folder not showing in FolderPicker. [#57156](https://github.com/grafana/grafana/pull/57156), [@eledobleefe](https://github.com/eledobleefe)
|
||||
- **Elasticsearch:** Fix calculation of trimEdges in alert mode. [#56148](https://github.com/grafana/grafana/pull/56148), [@jorgelbg](https://github.com/jorgelbg)
|
||||
- **Elasticsearch:** Fix trimEdges delete logic in alert mode. [#56985](https://github.com/grafana/grafana/pull/56985), [@gabor](https://github.com/gabor)
|
||||
- **GoogleOAuth:** Unlock User Admin UI. [#57350](https://github.com/grafana/grafana/pull/57350), [@gamab](https://github.com/gamab)
|
||||
- **LogContext:** Fix wrong color of `show context` icon in light theme. [#57427](https://github.com/grafana/grafana/pull/57427), [@svennergr](https://github.com/svennergr)
|
||||
- **Loki:** Fix adding of adhoc filters to stream selector when query with empty stream selector. [#57280](https://github.com/grafana/grafana/pull/57280), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Fix double stringified log-lines when copied via Copy button. [#57243](https://github.com/grafana/grafana/pull/57243), [@svennergr](https://github.com/svennergr)
|
||||
- **Loki:** Fix explain section about $\_\_interval variable. [#57188](https://github.com/grafana/grafana/pull/57188), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Remove already selected options from next label filter options in builder. [#57187](https://github.com/grafana/grafana/pull/57187), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **NodeGraph:** Fix rendering issues when values of arc are over 1. [#57460](https://github.com/grafana/grafana/pull/57460), [@aocenas](https://github.com/aocenas)
|
||||
- **PublicDashboards:** Fix hidden queries execution. [#57194](https://github.com/grafana/grafana/pull/57194), [@juanicabanas](https://github.com/juanicabanas)
|
||||
- **PublicDashboards:** Fix hidden queries execution. [#57004](https://github.com/grafana/grafana/pull/57004), [@juanicabanas](https://github.com/juanicabanas)
|
||||
- **Tempo:** Fix Node Graph visualization type in dashboard. [#56931](https://github.com/grafana/grafana/pull/56931), [@CrypticSignal](https://github.com/CrypticSignal)
|
||||
- **TimeSeries:** Fix stacking when first value is negative zero. [#57257](https://github.com/grafana/grafana/pull/57257), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **TimeseriesPanel:** Fix variables in data links. [#56729](https://github.com/grafana/grafana/pull/56729), [@mdvictor](https://github.com/mdvictor)
|
||||
- **User:** Fix externalUserId not being populated. [#57341](https://github.com/grafana/grafana/pull/57341), [@joshhunt](https://github.com/joshhunt)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
We added some components a bit too early to @grafana/ui in 9.2 so we are moving them back to @grafana/experimental. If you used any of these components
|
||||
|
||||
- AccessoryButton
|
||||
- EditorFieldGroup
|
||||
- EditorHeader
|
||||
- EditorField
|
||||
- EditorRow
|
||||
- EditorList
|
||||
- EditorRows
|
||||
- EditorSwitch
|
||||
- FlexItem
|
||||
- Stack
|
||||
- InlineSelect
|
||||
- InputGroup
|
||||
- Space
|
||||
|
||||
Please use them from grafana/experimental from now on. Issue [#57436](https://github.com/grafana/grafana/issues/57436)
|
||||
|
||||
<!-- 9.2.2 END -->
|
||||
<!-- 9.2.1 START -->
|
||||
|
||||
# 9.2.1 (2022-10-18)
|
||||
|
||||
@@ -20,7 +20,7 @@ COPY emails emails
|
||||
ENV NODE_ENV production
|
||||
RUN yarn build
|
||||
|
||||
FROM golang:1.19.2-alpine3.15 as go-builder
|
||||
FROM golang:1.19.3-alpine3.15 as go-builder
|
||||
|
||||
RUN apk add --no-cache gcc g++ make
|
||||
|
||||
@@ -57,8 +57,6 @@ ENV PATH="/usr/share/grafana/bin:$PATH" \
|
||||
WORKDIR $GF_PATHS_HOME
|
||||
|
||||
RUN apk add --no-cache ca-certificates bash tzdata musl-utils
|
||||
RUN apk add --no-cache openssl ncurses-libs ncurses-terminfo-base --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
|
||||
RUN apk upgrade ncurses-libs ncurses-terminfo-base --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
|
||||
RUN apk info -vv | sort
|
||||
|
||||
COPY conf ./conf
|
||||
|
||||
@@ -21,7 +21,7 @@ COPY emails emails
|
||||
ENV NODE_ENV production
|
||||
RUN yarn build
|
||||
|
||||
FROM golang:1.19.2 AS go-builder
|
||||
FROM golang:1.19.3 AS go-builder
|
||||
|
||||
WORKDIR /src/grafana
|
||||
|
||||
|
||||
@@ -210,10 +210,10 @@ check_for_updates = true
|
||||
check_for_plugin_updates = true
|
||||
|
||||
# Google Analytics universal tracking code, only enabled if you specify an id here
|
||||
google_analytics_ua_id =
|
||||
google_analytics_ua_id =
|
||||
|
||||
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
||||
google_analytics_4_id =
|
||||
google_analytics_4_id =
|
||||
|
||||
# Google Tag Manager ID, only enabled if you specify an id here
|
||||
google_tag_manager_id =
|
||||
@@ -626,6 +626,7 @@ key_file =
|
||||
role_attribute_path =
|
||||
role_attribute_strict = false
|
||||
auto_sign_up = false
|
||||
url_login = false
|
||||
allow_assign_grafana_admin = false
|
||||
|
||||
#################################### Auth LDAP ###########################
|
||||
|
||||
@@ -54,6 +54,8 @@ datasources:
|
||||
jsonData:
|
||||
manageAlerts: true
|
||||
alertmanagerUid: gdev-alertmanager
|
||||
prometheusType: Prometheus #Cortex | Mimir | Prometheus | Thanos
|
||||
prometheusVersion: 2.40.0
|
||||
|
||||
- name: gdev-slow-prometheus
|
||||
type: prometheus
|
||||
|
||||
@@ -5,6 +5,7 @@ aliases:
|
||||
- /docs/grafana/latest/enterprise/datasource_permissions/
|
||||
- /docs/grafana/latest/permissions/datasource_permissions/
|
||||
- /docs/grafana/latest/administration/data-source-management/
|
||||
- /docs/grafana/latest/enterprise/query-caching/
|
||||
title: Data source management
|
||||
description: Data source management information for Grafana administrators
|
||||
weight: 100
|
||||
@@ -46,7 +47,7 @@ To add a data source:
|
||||
|
||||
Data source permissions allow you to restrict access for users to query a data source. For each data source there is a permission page that allows you to enable permissions and restrict query permissions to specific **Users** and **Teams**.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud).
|
||||
|
||||
### Enable data source permissions
|
||||
|
||||
@@ -98,3 +99,86 @@ Note that _all_ existing permissions created for the data source will be deleted
|
||||
1. On the Permissions tab, click **Disable Permissions**.
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
## Query caching
|
||||
|
||||
When query caching is enabled, Grafana temporarily stores the results of data source queries. When you or another user submit the exact same query again, the results will come back from the cache instead of from the data source (like Splunk or ServiceNow) itself.
|
||||
|
||||
Query caching works for all backend data sources. You can enable the cache globally and configure the cache duration (also called Time to Live, or TTL).
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
The following cache backends are available: in-memory, Redis, and Memcached.
|
||||
|
||||
> **Note:** Storing cached queries in-memory can increase Grafana's memory footprint. In production environments, a Redis or Memcached backend is highly recommended.
|
||||
|
||||
When a panel queries a cached data source, the time until this query fetches fresh data is determined by the panel's **interval.** This means that wider panels and dashboards with shorter time ranges fetch new data more frequently than narrower panels and dashboards with longer time ranges.
|
||||
|
||||
Interval is visible in a panel's [query options]({{< relref "../../panels-visualizations/query-transform-data/" >}}). It is calculated like this: `(max data points) / time range`. Max data points are calculated based on the width of the panel. For example, a full-width panel on a dashboard with a time range of `last 7 days` will retrieve fresh data every 10 minutes. In this example, cached data for this panel will be served for up to 10 minutes before Grafana queries the data source again and returns new data.
|
||||
|
||||
You can make a panel retrieve fresh data more frequently by increasing the **Max data points** setting in the panel's [query options]({{< relref "../../panels-visualizations/query-transform-data/" >}}).
|
||||
|
||||
### Query caching benefits
|
||||
|
||||
- Faster dashboard load times, especially for popular dashboards.
|
||||
- Reduced API costs.
|
||||
- Reduced likelihood that APIs will rate-limit or throttle requests.
|
||||
|
||||
### Data sources that work with query caching
|
||||
|
||||
Query caching works for all [Enterprise data sources](https://grafana.com/grafana/plugins/?type=datasource&enterprise=1) as well as the following [built-in data sources]({{< relref "../../datasources/" >}}):
|
||||
|
||||
- CloudWatch Metrics
|
||||
- Google Cloud Monitoring
|
||||
- InfluxDB
|
||||
- Microsoft SQL Server
|
||||
- MySQL
|
||||
- Postgres
|
||||
- Tempo
|
||||
|
||||
Some data sources, such as Elasticsearch, Prometheus, and Loki, cache queries themselves, so Grafana query caching does not improve performance.
|
||||
|
||||
Query caching also works for all data sources that include a backend. More specifically, caching works with data sources that extend the `DataSourceWithBackend` class in the plugins SDK.
|
||||
|
||||
To tell if a data source works with query caching, follow the instructions below to **Enable and Configure query caching**. If caching is enabled in Grafana but the Caching tab is not visible for the given data source, then query caching is not available for that data source.
|
||||
|
||||
### Enable and configure query caching
|
||||
|
||||
You must be an Org admin or Grafana admin to enable query caching for a data source. For more information on Grafana roles and permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/" >}}).
|
||||
|
||||
By default, data source queries are not cached. To enable query caching for a single data source:
|
||||
|
||||
1. On the side menu, click Configuration > Data Sources.
|
||||
1. In the data source list, click the data source that you want to turn on caching for.
|
||||
1. Open the Cache tab.
|
||||
1. Press the Enable button.
|
||||
1. (Optional) Choose custom TTLs for the data source's queries and resources caching. If you skip this step, then Grafana uses the default TTL.
|
||||
|
||||
> **Note:** If query caching is enabled and the Cache tab is not visible in a data source's settings, then query caching is not available for that data source.
|
||||
|
||||
To configure global settings for query caching, refer to the [Query caching section of Enterprise Configuration]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}).
|
||||
|
||||
### Disable query caching
|
||||
|
||||
To disable query caching for a single data source:
|
||||
|
||||
1. On the side menu, click Configuration > Data Sources.
|
||||
1. In the data source list, click the data source that you want to turn off caching for.
|
||||
1. In the Cache tab, click Disable.
|
||||
|
||||
To disable query caching for an entire Grafana instance, set the `enabled` flag to `false` in the [Query caching section of Enterprise Configuration]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}). You will no longer see the Cache tab on any data sources, and no data source queries will be cached.
|
||||
|
||||
### Clear cache
|
||||
|
||||
If you experience performance issues or repeated queries become slower to execute, consider clearing your cache.
|
||||
|
||||
> **Note:** This action impacts all cache-enabled data sources. If you are using Memcached, the system clears all data from the Memcached instance.
|
||||
|
||||
1. Sign in to Grafana and click **Settings > Data Sources**.
|
||||
1. Select a data source.
|
||||
1. Click the **Cache** tab.
|
||||
1. Click **Clear cache**.
|
||||
|
||||
### Sending a request without cache
|
||||
|
||||
If a data source query request contains an `X-Cache-Skip` header, then Grafana skips the caching middleware, and does not search the cache for a response. This can be particularly useful when debugging data source queries using cURL.
|
||||
|
||||
@@ -20,7 +20,7 @@ weight: 500
|
||||
|
||||
# Grafana Enterprise license
|
||||
|
||||
When you become a Grafana Enterprise customer, you gain access to Grafana's premium observability features, including enterprise data source plugins, reporting, and role-based access control. In order to use these [enhanced features of Grafana Enterprise]({{< relref "../../enterprise/" >}}), you must purchase and activate a Grafana Enterprise license.
|
||||
When you become a Grafana Enterprise customer, you gain access to Grafana's premium observability features, including enterprise data source plugins, reporting, and role-based access control. In order to use these [enhanced features of Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}), you must purchase and activate a Grafana Enterprise license.
|
||||
|
||||
To purchase a license directly from Grafana Labs, [Contact a Grafana Labs representative](https://grafana.com/contact?about=grafana-enterprise). To activate an Enterprise license purchased from Grafana Labs, refer to [Activate an Enterprise license]({{< ref "#activate-an-enterprise-license" >}}).
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ Since not all datasources have the same configuration settings we only have the
|
||||
| httpMethod | string | Prometheus | HTTP Method. 'GET', 'POST', defaults to POST |
|
||||
| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. |
|
||||
| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI |
|
||||
| esVersion | string | Elasticsearch | Elasticsearch version (E.g. `7.0.0`, `7.6.1`) |
|
||||
| esVersion | string | Elasticsearch | Elasticsearch version (e.g. `7.0.0`, `7.6.1`) |
|
||||
| timeField | string | Elasticsearch | Which field that should be used as timestamp |
|
||||
| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' |
|
||||
| logMessageField | string | Elasticsearch | Which field should be used as the log message |
|
||||
@@ -189,6 +189,8 @@ Since not all datasources have the same configuration settings we only have the
|
||||
| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) |
|
||||
| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) |
|
||||
| keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with datasources |
|
||||
| prometheusVersion | string | Prometheus | The version of the Prometheus datasource (e.g. `2.37.0`, `2.24.0`) |
|
||||
| prometheusType | string | Prometheus | The type of the Prometheus datasources (i.e. `Prometheus`, `Cortex`, `Thanos`, or `Mimir`) |
|
||||
|
||||
#### Secure Json Data
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/enterprise/recorded-queries/
|
||||
- /docs/grafana/latest/administration/recorded-queries/
|
||||
description: Grafana Enterprise recorded queries
|
||||
keywords:
|
||||
- grafana
|
||||
@@ -17,7 +18,7 @@ Recorded queries allow you to see trends over time by taking a snapshot of a dat
|
||||
|
||||
For our plugins that do not return time series, it might be useful to plot historical data. For example, you might want to query ServiceNow to see a history of request response times but it can only return current point-in-time metrics.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../enterprise/" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
## How recorded queries work
|
||||
|
||||
@@ -147,7 +147,7 @@ For details on managing teams, see [Team management]({{< relref "../team-managem
|
||||
|
||||
While Grafana OSS includes a robust set of permissions and settings that you can use to manage user access to server and organization resources, you might find that you require additional capabilities.
|
||||
|
||||
[Grafana Enterprise]({{< relref "../../enterprise/">}}) provides the following permissions-related features:
|
||||
[Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise">}}) provides the following permissions-related features:
|
||||
|
||||
- Data source permissions
|
||||
- Role-based access control (RBAC)
|
||||
|
||||
@@ -90,7 +90,7 @@ Assign fixed roles when the basic roles do not meet your permission requirements
|
||||
- [Provisioning]({{< relref "../../provisioning/" >}})
|
||||
- [Reports]({{< relref "../../../dashboards/create-reports/" >}})
|
||||
- [Roles]({{< relref "../../" >}})
|
||||
- [Settings]({{< relref "../../../enterprise/settings-updates/" >}})
|
||||
- [Settings]({{< relref "../../../setup-grafana/configure-grafana/settings-updates-at-runtime" >}})
|
||||
- [Service accounts]({{< relref "../../service-accounts/" >}})
|
||||
- [Teams]({{< relref "../../team-management/" >}})
|
||||
- [Users]({{< relref "../../user-management/" >}})
|
||||
|
||||
@@ -116,7 +116,7 @@ The following list contains role-based access control actions.
|
||||
| `serviceaccounts.permissions:write` | `serviceaccounts:*` | Update Grafana service account permissions to control who can do what with the service account. |
|
||||
| `serviceaccounts.permissions:read` | `serviceaccounts:*` | Read Grafana service account permissions to see who can do what with the service account. |
|
||||
| `settings:read` | `settings:*`<br>`settings:auth.saml:*`<br>`settings:auth.saml:enabled` (property level) | Read the [Grafana configuration settings]({{< relref "../../../../setup-grafana/configure-grafana/" >}}) |
|
||||
| `settings:write` | `settings:*`<br>`settings:auth.saml:*`<br>`settings:auth.saml:enabled` (property level) | Update any Grafana configuration settings that can be [updated at runtime]({{< relref "../../../../enterprise/settings-updates/" >}}). |
|
||||
| `settings:write` | `settings:*`<br>`settings:auth.saml:*`<br>`settings:auth.saml:enabled` (property level) | Update any Grafana configuration settings that can be [updated at runtime]({{< relref "../../../../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}). |
|
||||
| `status:accesscontrol` | `services:accesscontrol` | Get access-control enabled status. |
|
||||
| `teams.permissions:read` | `teams:*`<br>`teams:id:*` | Read members and External Group Synchronization setup for teams. |
|
||||
| `teams.permissions:write` | `teams:*`<br>`teams:id:*` | Add, remove and update members and manage External Group Synchronization setup for teams. |
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
aliases:
|
||||
- /docs/grafana/latest/enterprise/access-control/rbac-provisioning/
|
||||
- /docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-provisioning/
|
||||
description: Learn about RBAC provisioning and view an example YAML provisioning file
|
||||
- /docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-grafana-provisioning
|
||||
description: Learn about RBAC Grafana provisioning and view an example YAML provisioning file
|
||||
that configures Grafana role assignments.
|
||||
menuTitle: RBAC provisioning
|
||||
title: Grafana RBAC provisioning
|
||||
menuTitle: Provisioning RBAC with Grafana
|
||||
title: Provisioning RBAC with Grafana
|
||||
weight: 60
|
||||
---
|
||||
|
||||
# Grafana RBAC provisioning
|
||||
# Provisioning RBAC with Grafana
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
|
||||
@@ -117,3 +118,9 @@ teams:
|
||||
# <string> state of the assignment. Defaults to 'present'. If 'absent', the assignment will be revoked.
|
||||
state: absent
|
||||
```
|
||||
|
||||
## Useful Links
|
||||
|
||||
[Provisioning RBAC setup with Terraform]({{< relref "./rbac-terraform-provisioning">}})
|
||||
|
||||
[Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/)
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/enterprise/access-control/rbac-provisioning/rbac-terraform-provisioning
|
||||
description: Learn about RBAC Terraform provisioning and view an example of provisioning configuration
|
||||
for Grafana roles and role assignments.
|
||||
menuTitle: Provisioning RBAC with Terraform
|
||||
title: Provisioning RBAC with Terraform
|
||||
weight: 60
|
||||
---
|
||||
|
||||
# Provisioning RBAC with Terraform
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
|
||||
You can create, change or remove [Custom roles](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role) and create or remove [basic and custom role assignments](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role_assignment), by using [Terraform's Grafana provider](https://registry.terraform.io/providers/grafana/grafana/latest/docs).
|
||||
|
||||
## Before you begin
|
||||
|
||||
- Ensure you have the grafana/grafana [Terraform provider](https://registry.terraform.io/providers/grafana/grafana/) 1.29.0 or higher.
|
||||
|
||||
- Ensure you are using Grafana 9.2 or higher.
|
||||
|
||||
## Create a Service Account Token for provisioning
|
||||
|
||||
We recommend using service account tokens for provisioning. [Service accounts](https://grafana.com/docs/grafana/latest/administration/service-accounts/) support fine grained permissions, which allows you to easily authenticate and use the minimum set of permissions needed to provision your RBAC infrastructure.
|
||||
|
||||
To create a service account token for provisioning, complete the following steps.
|
||||
|
||||
1. [Create a new service account](https://grafana.com/docs/grafana/latest/administration/service-accounts/#create-a-service-account-in-grafana) for your CI pipeline.
|
||||
1. [Assign permissions to service account](https://grafana.com/docs/grafana/latest/administration/service-accounts/#assign-roles-to-a-service-account-in-grafana):
|
||||
1. You will need roles “Role reader”, "Role writer" and roles including any permissions that will be provisioned. For example, to create or assign a role that allows creating users, a service account needs permissions to create users.
|
||||
1. Alternatively, you can assign "Admin" basic role to the service account.
|
||||
1. [Create a new service account token](https://grafana.com/docs/grafana/latest/administration/service-accounts/#to-add-a-token-to-a-service-account) for use in Terraform.
|
||||
|
||||
Alternatively, you can use basic authentication. To view all the supported authentication formats, see [here](https://registry.terraform.io/providers/grafana/grafana/latest/docs#authentication).
|
||||
|
||||
## Configure the Terraform provider
|
||||
|
||||
RBAC support is included as part of the [Grafana Terraform provider](https://registry.terraform.io/providers/grafana/grafana/latest/docs).
|
||||
|
||||
The following is an example you can use to configure the Terraform provider.
|
||||
|
||||
```terraform
|
||||
terraform {
|
||||
required_providers {
|
||||
grafana = {
|
||||
source = "grafana/grafana"
|
||||
version = ">= 1.29.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "grafana" {
|
||||
url = <YOUR_GRAFANA_URL>
|
||||
auth = <YOUR_GRAFANA_SERVICE_ACCOUNT_TOKEN>
|
||||
}
|
||||
```
|
||||
|
||||
## Provision custom roles
|
||||
|
||||
The following example shows how to provision a custom role with some permissions.
|
||||
|
||||
1. Copy this code block into a .tf file on your local machine.
|
||||
|
||||
```terraform
|
||||
resource "grafana_role" "my_new_role" {
|
||||
name = "my_new_role"
|
||||
description = "My test role"
|
||||
version = 1
|
||||
uid = "newroleuid"
|
||||
global = true
|
||||
|
||||
permissions {
|
||||
action = "org.users:add"
|
||||
scope = "users:*"
|
||||
}
|
||||
permissions {
|
||||
action = "org.users:write"
|
||||
scope = "users:*"
|
||||
}
|
||||
permissions {
|
||||
action = "org.users:read"
|
||||
scope = "users:*"
|
||||
}
|
||||
permissions {
|
||||
action = "teams:create"
|
||||
}
|
||||
permissions {
|
||||
action = "teams:read"
|
||||
scope = "teams:*"
|
||||
}
|
||||
permissions {
|
||||
action = "teams:write"
|
||||
scope = "teams:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Run the command `terraform apply`.
|
||||
3. Go to Grafana's UI and check that the new role appears in the role picker:
|
||||

|
||||
|
||||
## Provision role assignments
|
||||
|
||||
The following example shows how to provision role assignments.
|
||||
In this example a team, user and service account are provisioned, and the custom role from the previous example is assigned to them.
|
||||
|
||||
1. Extend the configuration file from the [previous example]({{##}}) with the following:
|
||||
|
||||
```terraform
|
||||
resource "grafana_team" "test_team" {
|
||||
name = "terraform_test_team"
|
||||
}
|
||||
|
||||
resource "grafana_user" "test_user" {
|
||||
email = "terraform_user@test.com"
|
||||
login = "terraform_test_user"
|
||||
password = <TEST_PASSWORD>
|
||||
}
|
||||
|
||||
resource "grafana_service_account" "test_sa" {
|
||||
name = "terraform_test_sa"
|
||||
role = "Viewer"
|
||||
}
|
||||
|
||||
resource "grafana_role_assignment" "my_new_role_assignment" {
|
||||
role_uid = grafana_role.my_new_role.uid
|
||||
users = [grafana_user.test_user.id]
|
||||
teams = [grafana_team.test_team.id]
|
||||
service_accounts = [grafana_service_account.test_sa.id]
|
||||
}
|
||||
```
|
||||
|
||||
1. Substitute `<TEST_PASSWORD>` with a test password for your test user.
|
||||
|
||||
1. Run the command `terraform apply`.
|
||||
|
||||
1. Go to Grafana's UI and check that a user, team and service account have been created, and that the role has been assigned to them:
|
||||

|
||||

|
||||

|
||||
|
||||
Note that instead of using a provisioned role, you can also look up the `uid` of an already existing fixed or custom role and use that instead.
|
||||
You can use the [API endpoint for listing roles](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/#create-and-manage-custom-roles) to look up role `uid`s.
|
||||
Similarly, you can look up and use `id`s of users, teams and service accounts that have not been provisioned to assign roles to them.
|
||||
|
||||
## Useful Links
|
||||
|
||||
[RBAC setup with Grafana provisioning]({{< relref "./rbac-grafana-provisioning">}})
|
||||
|
||||
[Grafana Cloud Terraform provisioning](https://grafana.com/docs/grafana-cloud/infrastructure-as-code/terraform/)
|
||||
@@ -27,7 +27,7 @@ A common use case for creating a service account is to perform operations on aut
|
||||
- Set up an external SAML authentication provider
|
||||
- Interact with Grafana without signing in as a user
|
||||
|
||||
In [Grafana Enterprise]({{< relref "../../enterprise/" >}}), you can also use service accounts in combination with [role-based access control]({{< relref "../roles-and-permissions/access-control/" >}}) to grant very specific permissions to applications that interact with Grafana.
|
||||
In [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}), you can also use service accounts in combination with [role-based access control]({{< relref "../roles-and-permissions/access-control/" >}}) to grant very specific permissions to applications that interact with Grafana.
|
||||
|
||||
> **Note:** Service accounts can only act in the organization they are created for. If you have the same task that is needed for multiple organizations, we recommend creating service accounts in each organization.
|
||||
|
||||
@@ -106,7 +106,7 @@ You can create a service account token using the Grafana UI or via the API. For
|
||||
You can assign roles to a Grafana service account to control access for the associated service account tokens.
|
||||
You can assign roles to a service account using the Grafana UI or via the API. For more information about assigning a role to a service account via the API, refer to [Update service account using the HTTP API]({{< relref "../../developers/http_api/serviceaccount/#update-service-account" >}}).
|
||||
|
||||
In [Grafana Enterprise]({{< relref "../../enterprise/" >}}), you can also [assign RBAC roles]({{< relref "../roles-and-permissions/access-control/assign-rbac-roles" >}}) to grant very specific permissions to applications that interact with Grafana.
|
||||
In [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}), you can also [assign RBAC roles]({{< relref "../roles-and-permissions/access-control/assign-rbac-roles" >}}) to grant very specific permissions to applications that interact with Grafana.
|
||||
|
||||
### Before you begin
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ The following table lists the contact point types supported by Grafana.
|
||||
| [Pagerduty](https://www.pagerduty.com/) | `pagerduty` | Supported | Supported |
|
||||
| [Prometheus Alertmanager](https://prometheus.io) | `prometheus-alertmanager` | Supported | N/A |
|
||||
| [Pushover](https://pushover.net/) | `pushover` | Supported | Supported |
|
||||
| [Sensu](https://sensu.io/) | `sensu` | Supported | N/A |
|
||||
| [Sensu Go](https://docs.sensu.io/sensu-go/) | `sensugo` | Supported | N/A |
|
||||
| [Slack](https://slack.com/) | `slack` | Supported | Supported |
|
||||
| [Telegram](https://telegram.org/) | `telegram` | Supported | N/A |
|
||||
|
||||
@@ -76,7 +76,7 @@ Only organization administrators can create reports by default. You can customiz
|
||||
|
||||
### Choose template variables
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 7.5 and later behind the `reportVariables` feature flag, Grafana Enterprise version 8.0 and later without a feature flag, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.5 and later behind the `reportVariables` feature flag, Grafana Enterprise version 8.0 and later without a feature flag, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can configure report-specific template variables for the dashboard on the report page. The variables that you select will override the variables from the dashboard, and they are used when rendering a PDF file of the report. For detailed information about using template variables, refer to the [Templates and variables]({{< relref "../variables/" >}}) section.
|
||||
|
||||
@@ -84,7 +84,7 @@ You can configure report-specific template variables for the dashboard on the re
|
||||
|
||||
### Render a report with panels or rows set to repeat by a variable
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can include dynamic dashboards with panels or rows, set to repeat by a variable, into reports. For detailed information about setting up repeating panels or rows in dashboards, refer to [Repeat panels or rows]({{< relref "../../panels-visualizations/configure-panel-options/#configure-repeating-rows-or-panels" >}}).
|
||||
|
||||
@@ -99,7 +99,7 @@ You can include dynamic dashboards with panels or rows, set to repeat by a varia
|
||||
|
||||
### Report time range
|
||||
|
||||
> **Note:** You can set custom report time ranges in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) 7.2+ and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** You can set custom report time ranges in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 7.2+ and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
By default, reports use the saved time range of the dashboard. You can change the time range of the report by:
|
||||
|
||||
@@ -123,7 +123,7 @@ If the time zone is set differently between your Grafana server and its remote i
|
||||
|
||||
### CSV export
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) 8+ with the [Grafana image renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer) v3.0+, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 8+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can attach a CSV file to the report email for each table panel on the selected dashboard, along with the PDF report. By default, CSVs larger than 10Mb are not sent which keeps email servers from rejecting the email. You can increase or decrease this limit in the [reporting configuration]({{< relref "#rendering-configuration" >}}).
|
||||
|
||||
@@ -135,7 +135,7 @@ A background job runs every 10 minutes and removes temporary CSV files. You can
|
||||
|
||||
### Scheduling
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
> The scheduler was significantly changed in Grafana Enterprise version 8.1.
|
||||
|
||||
Scheduled reports can be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**, for example to send the report via the API.
|
||||
@@ -158,7 +158,7 @@ When you schedule a report with a monthly frequency, and set the start date betw
|
||||
|
||||
#### Send a test email
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
1. In the report, click **Send test email**.
|
||||
1. In the **Email** field, enter the email address or addresses that you want to test, separated by a semicolon.
|
||||
@@ -171,19 +171,19 @@ The last saved version of the report will be sent to selected emails. You can us
|
||||
|
||||
### Pause a report
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can pause sending reports from the report list view by clicking the pause icon. The report will not be sent according to its schedule until it is resumed by clicking the resume button on the report row.
|
||||
|
||||
### Add multiple dashboards to a report
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced]({{< relref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can add more than one dashboard to a report. Additional dashboards will be rendered as new pages in the same PDF file, or additional images if you chose to embed images in your report email. You cannot add the same dashboard to a report multiple times.
|
||||
|
||||
### Embed a dashboard as an image into a report
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced]({{< relref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can send a report email with an image of the dashboard embedded in the email instead of attached as a PDF. In this case, the email recipients can see the dashboard at a glance instead of having to open the PDF.
|
||||
|
||||
@@ -238,7 +238,7 @@ font_italic = DejaVuSansCondensed-Oblique.ttf
|
||||
|
||||
## Report settings
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 7.2 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.2 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
You can configure organization-wide report settings in the **Settings** tab on the **Reporting** page. Settings are applied to all the reports for current organization.
|
||||
|
||||
|
||||
@@ -21,25 +21,27 @@ Grafana includes built-in support for Prometheus. This topic explains options, v
|
||||
|
||||
To access Prometheus settings, hover your mouse over the **Configuration** (gear) icon, then click **Data Sources**, and then click the Prometheus data source.
|
||||
|
||||
| Name | Description |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Name` | The data source name. This is how you refer to the data source in panels and queries. |
|
||||
| `Default` | Default data source that is pre-selected for new panels. |
|
||||
| `Url` | The URL of your Prometheus server, for example, `http://prometheus.example.org:9090`. |
|
||||
| `Access` | Only Server access mode is functional. If Server mode is already selected this option is hidden. Otherwise change to Server mode to prevent errors. |
|
||||
| `Basic Auth` | Enable basic authentication to the Prometheus data source. |
|
||||
| `User` | User name for basic authentication. |
|
||||
| `Password` | Password for basic authentication. |
|
||||
| `Scrape interval` | Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s. |
|
||||
| `HTTP method` | Use either POST or GET HTTP method to query your data source. POST is the recommended and pre-selected method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network. |
|
||||
| `Disable metrics lookup` | Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances. |
|
||||
| `Custom Query Parameters` | Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. |
|
||||
| **Exemplars configuration** | |
|
||||
| `Internal link` | Enable this option is you have an internal link. When you enable this option, you will see a data source selector. Select the backend tracing data store for your exemplar data. |
|
||||
| `Data source` | You will see this option only if you enable `Internal link` option. Select the backend tracing data store for your exemplar data. |
|
||||
| `URL` | You will see this option only if the `Internal link` option is disabled. Enter the full URL of the external link. You can interpolate the value from the field with `${__value.raw }` macro. |
|
||||
| `URL Label` | (Optional) add a custom display label to override the value of the `Label name` field. |
|
||||
| `Label name` | Add a name for the exemplar traceID property. |
|
||||
| Name | Description |
|
||||
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Name` | The data source name. This is how you refer to the data source in panels and queries. |
|
||||
| `Default` | Default data source that is pre-selected for new panels. |
|
||||
| `Url` | The URL of your Prometheus server, for example, `http://prometheus.example.org:9090`. |
|
||||
| `Access` | Only Server access mode is functional. If Server mode is already selected this option is hidden. Otherwise change to Server mode to prevent errors. |
|
||||
| `Basic Auth` | Enable basic authentication to the Prometheus data source. |
|
||||
| `User` | User name for basic authentication. |
|
||||
| `Password` | Password for basic authentication. |
|
||||
| `Scrape interval` | Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s. |
|
||||
| `HTTP method` | Use either POST or GET HTTP method to query your data source. POST is the recommended and pre-selected method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network. |
|
||||
| `Type` | The type of your Prometheus server, i.e `Prometheus`, `Cortex`, `Thanos` or `Mimir`. When this value is selected in the configuration UI, the Prometheus version field attempts to detect the version automatically using the Prometheus [buildinfo](https://semver.org/) API. Some Prometheus types do not support this API, and you will need to manually populate the version in those cases (e.g. Cortex). |
|
||||
| `Version` | The version of your Prometheus server, note that this field is not visible until the Prometheus type is selected. |
|
||||
| `Disable metrics lookup` | Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances. |
|
||||
| `Custom Query Parameters` | Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. |
|
||||
| **Exemplars configuration** | |
|
||||
| `Internal link` | Enable this option is you have an internal link. When you enable this option, you will see a data source selector. Select the backend tracing data store for your exemplar data. |
|
||||
| `Data source` | You will see this option only if you enable `Internal link` option. Select the backend tracing data store for your exemplar data. |
|
||||
| `URL` | You will see this option only if the `Internal link` option is disabled. Enter the full URL of the external link. You can interpolate the value from the field with `${__value.raw }` macro. |
|
||||
| `URL Label` | (Optional) add a custom display label to override the value of the `Label name` field. |
|
||||
| `Label name` | Add a name for the exemplar traceID property. |
|
||||
|
||||
## Prometheus query editor
|
||||
|
||||
@@ -176,13 +178,13 @@ types of template variables.
|
||||
Variable of the type _Query_ allows you to query Prometheus for a list of metrics, labels or label values. The Prometheus data source plugin
|
||||
provides the following functions you can use in the `Query` input field.
|
||||
|
||||
| Name | Description | Used API endpoints |
|
||||
| ----------------------------- | ----------------------------------------------------------------------- | --------------------------------- |
|
||||
| `label_names()` | Returns a list of label names. | /api/v1/labels |
|
||||
| `label_values(label)` | Returns a list of label values for the `label` in every metric. | /api/v1/label/`label`/values |
|
||||
| `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. | /api/v1/series |
|
||||
| `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
|
||||
| `query_result(query)` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
|
||||
| Name | Description | Used API endpoints |
|
||||
| ----------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `label_names()` | Returns a list of label names. | /api/v1/labels |
|
||||
| `label_values(label)` | Returns a list of label values for the `label` in every metric. | /api/v1/label/`label`/values |
|
||||
| `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. | /api/v1/series or /api/v1/label/`label`/values, depending on prometheus type and version in datasource configuration |
|
||||
| `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
|
||||
| `query_result(query)` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
|
||||
|
||||
For details of what _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).
|
||||
|
||||
@@ -282,6 +284,8 @@ datasources:
|
||||
url: http://localhost:9090
|
||||
jsonData:
|
||||
httpMethod: POST
|
||||
prometheusType: Prometheus
|
||||
prometheusVersion: 2.37.0
|
||||
exemplarTraceIdDestinations:
|
||||
# Field with internal link pointing to data source in Grafana.
|
||||
# datasourceUid value can be anything, but it should be unique across all defined data source uids.
|
||||
|
||||
@@ -17,7 +17,7 @@ title: RBAC HTTP API
|
||||
|
||||
# RBAC API
|
||||
|
||||
> Role-based access control API is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
> Role-based access control API is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
The API can be used to create, update, delete, get, and list roles.
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ Here's a cURL command that works for getting the home dashboard when you are run
|
||||
curl http://admin:admin@localhost:3000/api/search
|
||||
```
|
||||
|
||||
To pass a username and password with [HTTP basic authorization]({{< relref "/administration/roles-and-permissions/access-control/manage-rbac-roles/" >}}), encode them as base64.
|
||||
To pass a username and password with [HTTP basic authorization]({{< relref "../../administration/roles-and-permissions/access-control/manage-rbac-roles/" >}}), encode them as base64.
|
||||
You can't use authorization tokens in the request.
|
||||
|
||||
For example, to [list permissions associated with roles]({{< relref "/administration/roles-and-permissions/access-control/manage-rbac-roles/" >}}) given a username of `user` and password of `password`, use:
|
||||
For example, to [list permissions associated with roles]({{< relref "../../administration/roles-and-permissions/access-control/manage-rbac-roles/" >}}) given a username of `user` and password of `password`, use:
|
||||
|
||||
```
|
||||
curl --location --request GET '<grafana_url>/api/access-control/builtin-roles' --header 'Authorization: Basic dXNlcjpwYXNzd29yZAo='
|
||||
|
||||
@@ -19,7 +19,7 @@ title: 'Datasource Permissions HTTP API'
|
||||
|
||||
# Data Source Permissions API
|
||||
|
||||
> The Data Source Permissions is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
> The Data Source Permissions is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "../../administration/roles-and-permissions/access-control/custom-role-actions-scopes/" >}}) for more information.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ title: 'External Group Sync HTTP API'
|
||||
|
||||
# External Group Synchronization API
|
||||
|
||||
> External Group Synchronization is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
> External Group Synchronization is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "../../administration/roles-and-permissions/access-control/custom-role-actions-scopes/" >}}) for more information.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ title: 'Licensing HTTP API'
|
||||
|
||||
# Enterprise License API
|
||||
|
||||
Licensing is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
Licensing is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "../../administration/roles-and-permissions/access-control/custom-role-actions-scopes/" >}}) for more information.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ This API allows you to interact programmatically with the [Reporting]({{< relref
|
||||
|
||||
> The Reporting API is not stabilized yet, it is still in active development and may change without prior notice.
|
||||
|
||||
> Reporting is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
> Reporting is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "../../administration/roles-and-permissions/access-control/custom-role-actions-scopes/" >}}) for more information.
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/enterprise/query-caching/
|
||||
description: Grafana Enterprise data source query caching
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- query
|
||||
- caching
|
||||
title: Query caching
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Query caching
|
||||
|
||||
When query caching is enabled, Grafana temporarily stores the results of data source queries. When you or another user submit the exact same query again, the results will come back from the cache instead of from the data source (like Splunk or ServiceNow) itself.
|
||||
|
||||
Query caching works for all backend data sources. You can enable the cache globally and configure the cache duration (also called Time to Live, or TTL).
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../enterprise/" >}}) and [Grafana Cloud Pro and Advanced]({{< ref "/grafana-cloud" >}}).
|
||||
|
||||
The following cache backends are available: in-memory, Redis, and Memcached.
|
||||
|
||||
> **Note:** Storing cached queries in-memory can increase Grafana's memory footprint. In production environments, a Redis or Memcached backend is highly recommended.
|
||||
|
||||
When a panel queries a cached data source, the time until this query fetches fresh data is determined by the panel's **interval.** This means that wider panels and dashboards with shorter time ranges fetch new data more frequently than narrower panels and dashboards with longer time ranges.
|
||||
|
||||
Interval is visible in a panel's [query options]({{< relref "../panels-visualizations/query-transform-data/" >}}). It is calculated like this: `(max data points) / time range`. Max data points are calculated based on the width of the panel. For example, a full-width panel on a dashboard with a time range of `last 7 days` will retrieve fresh data every 10 minutes. In this example, cached data for this panel will be served for up to 10 minutes before Grafana queries the data source again and returns new data.
|
||||
|
||||
You can make a panel retrieve fresh data more frequently by increasing the **Max data points** setting in the panel's [query options]({{< relref "../panels-visualizations/query-transform-data/" >}}).
|
||||
|
||||
## Query caching benefits
|
||||
|
||||
- Faster dashboard load times, especially for popular dashboards.
|
||||
- Reduced API costs.
|
||||
- Reduced likelihood that APIs will rate-limit or throttle requests.
|
||||
|
||||
## Data sources that work with query caching
|
||||
|
||||
Query caching works for all [Enterprise data sources](https://grafana.com/grafana/plugins/?type=datasource&enterprise=1) as well as the following [built-in data sources]({{< relref "../datasources/" >}}):
|
||||
|
||||
- CloudWatch Metrics
|
||||
- Google Cloud Monitoring
|
||||
- InfluxDB
|
||||
- Microsoft SQL Server
|
||||
- MySQL
|
||||
- Postgres
|
||||
- Tempo
|
||||
|
||||
Some data sources, such as Elasticsearch, Prometheus, and Loki, cache queries themselves, so Grafana query caching does not improve performance.
|
||||
|
||||
Query caching also works for all data sources that include a backend. More specifically, caching works with data sources that extend the `DataSourceWithBackend` class in the plugins SDK.
|
||||
|
||||
To tell if a data source works with query caching, follow the instructions below to **Enable and Configure query caching**. If caching is enabled in Grafana but the Caching tab is not visible for the given data source, then query caching is not available for that data source.
|
||||
|
||||
## Enable and configure query caching
|
||||
|
||||
You must be an Org admin or Grafana admin to enable query caching for a data source. For more information on Grafana roles and permissions, refer to [About users and permissions]({{< relref "../administration/roles-and-permissions/" >}}).
|
||||
|
||||
By default, data source queries are not cached. To enable query caching for a single data source:
|
||||
|
||||
1. On the side menu, click Configuration > Data Sources.
|
||||
1. In the data source list, click the data source that you want to turn on caching for.
|
||||
1. Open the Cache tab.
|
||||
1. Press the Enable button.
|
||||
1. (Optional) Choose custom TTLs for the data source's queries and resources caching. If you skip this step, then Grafana uses the default TTL.
|
||||
|
||||
> **Note:** If query caching is enabled and the Cache tab is not visible in a data source's settings, then query caching is not available for that data source.
|
||||
|
||||
To configure global settings for query caching, refer to the [Query caching section of Enterprise Configuration]({{< relref "../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}).
|
||||
|
||||
## Disable query caching
|
||||
|
||||
To disable query caching for a single data source:
|
||||
|
||||
1. On the side menu, click Configuration > Data Sources.
|
||||
1. In the data source list, click the data source that you want to turn off caching for.
|
||||
1. In the Cache tab, click Disable.
|
||||
|
||||
To disable query caching for an entire Grafana instance, set the `enabled` flag to `false` in the [Query caching section of Enterprise Configuration]({{< relref "../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}). You will no longer see the Cache tab on any data sources, and no data source queries will be cached.
|
||||
|
||||
## Clear cache
|
||||
|
||||
If you experience performance issues or repeated queries become slower to execute, consider clearing your cache.
|
||||
|
||||
> **Note:** This action impacts all cache-enabled data sources. If you are using Memcached, the system clears all data from the Memcached instance.
|
||||
|
||||
**To clear the cache**:
|
||||
|
||||
1. Sign in to Grafana and click **Settings > Data Sources**.
|
||||
1. Select a data source.
|
||||
1. Click the **Cache** tab.
|
||||
1. Click **Clear cache**.
|
||||
|
||||
## Sending a request without cache
|
||||
|
||||
If a data source query request contains an `X-Cache-Skip` header, then Grafana skips the caching middleware, and does not search the cache for a response. This can be particularly useful when debugging data source queries using cURL.
|
||||
@@ -11,8 +11,6 @@ weight: 400
|
||||
|
||||
{{< docs/shared "influxdb/intro.md" >}}
|
||||
|
||||
> **Note:** You can also configure a [Grafana Cloud](https://grafana.com/docs/grafana-cloud/) instance to display system metrics without having to host Grafana yourself. Grafana offers a [free account with Grafana Cloud](https://grafana.com/signup/cloud/connect-account?pg=gsdocs) to help you get started.
|
||||
|
||||
{{< docs/shared "getting-started/first-step.md" >}}
|
||||
|
||||
#### Get InfluxDB
|
||||
|
||||
@@ -13,8 +13,6 @@ weight: 300
|
||||
|
||||
Prometheus is an open source monitoring system for which Grafana provides out-of-the-box support. This topic walks you through the steps to create a series of dashboards in Grafana to display system metrics for a server monitored by Prometheus.
|
||||
|
||||
> **Note:** You can configure a [Grafana Cloud](https://grafana.com/docs/grafana-cloud/) instance to display system metrics without having to host Grafana yourself. A [free forever plan](https://grafana.com/signup/cloud/connect-account?pg=gsdocs) provides 10,000 active series for metrics.
|
||||
|
||||
{{< docs/shared "getting-started/first-step.md" >}}
|
||||
|
||||
#### Download Prometheus and node_exporter
|
||||
|
||||
@@ -51,7 +51,7 @@ Discover hundreds of [dashboards](https://grafana.com/grafana/dashboards) and [p
|
||||
|
||||
Grafana supports different authentication methods, such as LDAP and OAuth, and allows you to map users to organizations. Refer to the [User authentication overview]({{< relref "../setup-grafana/configure-security/configure-authentication/" >}}) for more information.
|
||||
|
||||
In Grafana Enterprise, you can also map users to teams: If your company has its own authentication system, Grafana allows you to map the teams in your internal systems to teams in Grafana. That way, you can automatically give people access to the dashboards designated for their teams. Refer to [Grafana Enterprise]({{< relref "../enterprise/_index.md" >}}) for more information.
|
||||
In Grafana Enterprise, you can also map users to teams: If your company has its own authentication system, Grafana allows you to map the teams in your internal systems to teams in Grafana. That way, you can automatically give people access to the dashboards designated for their teams. Refer to [Grafana Enterprise]({{< relref "./grafana-enterprise" >}}) for more information.
|
||||
|
||||
## Provisioning
|
||||
|
||||
@@ -61,7 +61,7 @@ For example, if you're spinning up a new Kubernetes cluster, you can also spin u
|
||||
|
||||
## Permissions
|
||||
|
||||
When organizations have one Grafana and multiple teams, they often want the ability to both keep things separate and share dashboards. You can create a team of users and then set permissions on [folders and dashboards]({{< relref "../administration/user-management/manage-dashboard-permissions/" >}}), and down to the [data source level]({{< relref "../administration/data-source-management#data-source-permissions" >}}) if you're using [Grafana Enterprise]({{< relref "../enterprise/" >}}).
|
||||
When organizations have one Grafana and multiple teams, they often want the ability to both keep things separate and share dashboards. You can create a team of users and then set permissions on [folders and dashboards]({{< relref "../administration/user-management/manage-dashboard-permissions/" >}}), and down to the [data source level]({{< relref "../administration/data-source-management#data-source-permissions" >}}) if you're using [Grafana Enterprise]({{< relref "./grafana-enterprise" >}}).
|
||||
|
||||
## Other Grafana Labs OSS Projects
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ To learn more about Grafana Enterprise, refer to [our product page](https://graf
|
||||
|
||||
## Enterprise features in Grafana Cloud
|
||||
|
||||
Many Grafana Enterprise features are also available in [Grafana Cloud]({{< ref "/docs/grafana-cloud" >}}) Pro and Advanced accounts. For details, refer to [the Grafana Cloud features table](https://grafana.com/pricing/#featuresTable) and [Enterprise features available to Grafana Cloud Pro and Advanced accounts]({{< ref "/docs/grafana-cloud/reference/enterprise-features" >}}).
|
||||
Many Grafana Enterprise features are also available in [Grafana Cloud]({{< ref "/docs/grafana-cloud" >}}) Pro and Advanced accounts. For details, refer to [the Grafana Cloud features table](https://grafana.com/pricing/#featuresTable).
|
||||
|
||||
## Authentication
|
||||
|
||||
@@ -55,16 +55,16 @@ Grafana Enterprise adds the following features:
|
||||
|
||||
- [Role-based access control]({{< relref "../administration/roles-and-permissions/access-control/" >}}) to control access with role-based permissions.
|
||||
- [Data source permissions]({{< relref "../administration/data-source-management#data-source-permissions" >}}) to restrict query access to specific teams and users.
|
||||
- [Data source query caching]({{< relref "../enterprise/query-caching.md" >}}) to temporarily store query results in Grafana to reduce data source load and rate limiting.
|
||||
- [Data source query caching]({{< relref "../administration/data-source-management/#query-caching" >}}) to temporarily store query results in Grafana to reduce data source load and rate limiting.
|
||||
- [Reporting]({{< relref "../dashboards/create-reports/" >}}) to generate a PDF report from any dashboard and set up a schedule to have it emailed to whomever you choose.
|
||||
- [Export dashboard as PDF]({{< relref "../dashboards/share-dashboards-panels/#export-dashboard-as-pdf" >}})
|
||||
- [Custom branding]({{< relref "../setup-grafana/configure-grafana/configure-custom-branding/" >}}) to customize Grafana from the brand and logo to the footer links.
|
||||
- [Usage insights]({{< relref "../dashboards/assess-dashboard-usage/" >}}) to understand how your Grafana instance is used.
|
||||
- [Recorded queries]({{< relref "../enterprise/recorded-queries" >}}) to see trends over time for your data sources.
|
||||
- [Recorded queries]({{< relref "../administration/recorded-queries" >}}) to see trends over time for your data sources.
|
||||
- [Vault integration]({{< relref "../setup-grafana/configure-security/configure-database-encryption/encrypt-secrets-using-hashicorp-key-vault/" >}}) to manage your configuration or provisioning secrets with Vault.
|
||||
- [Auditing]({{< relref "../setup-grafana/configure-security/audit-grafana/" >}}) tracks important changes to your Grafana instance to help you manage and mitigate suspicious activity and meet compliance requirements.
|
||||
- [Request security]({{< relref "../setup-grafana/configure-security/configure-request-security/" >}}) makes it possible to restrict outgoing requests from the Grafana server.
|
||||
- [Settings updates at runtime]({{< relref "../enterprise/settings-updates.md" >}}) allows you to update Grafana settings at runtime without requiring a restart.
|
||||
- [Settings updates at runtime]({{< relref "../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}) allows you to update Grafana settings at runtime without requiring a restart.
|
||||
|
||||
## Enterprise data sources
|
||||
|
||||
|
||||
@@ -359,11 +359,9 @@ The result after applying the inner join transformation looks like the following
|
||||
|
||||
#### Outer join
|
||||
|
||||
An outer join includes all data from an inner join and rows where values do not match in every input.
|
||||
An outer join includes all data from an inner join and rows where values do not match in every input. While the inner join joins Query A and Query B on the time field, the outer join includes all rows that don’t match on the time field.
|
||||
|
||||
Use this transformation to combine the results from multiple queries (combining on a passed join field or the first time column) into one result, and drop rows where a successful join cannot occur - performing an inner join.
|
||||
|
||||
In the following example, two queries return table data. It is visualized as two tables before applying the inner join transformation.
|
||||
In the following example, two queries return table data. It is visualized as two tables before applying the outer join transformation.
|
||||
|
||||
Query A:
|
||||
|
||||
@@ -381,10 +379,12 @@ Query B:
|
||||
| 2020-07-07 11:24:20 | server 2 | 5 |
|
||||
| 2020-07-07 11:04:20 | server 3 | 10 |
|
||||
|
||||
The result after applying the inner join transformation looks like the following:
|
||||
The result after applying the outer join transformation looks like the following:
|
||||
|
||||
| Time | Job | Uptime | Server | Errors |
|
||||
| ------------------- | ------- | --------- | -------- | ------ |
|
||||
| 2020-07-07 11:04:20 | | | server 3 | 10 |
|
||||
| 2020-07-07 11:14:20 | postgre | 345001233 | | |
|
||||
| 2020-07-07 11:34:20 | node | 25260122 | server 1 | 15 |
|
||||
| 2020-07-07 11:24:20 | postgre | 123001233 | server 2 | 5 |
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ password = $__file{/etc/secrets/gf_sql_password}
|
||||
|
||||
The `vault` provider allows you to manage your secrets with [Hashicorp Vault](https://www.hashicorp.com/products/vault).
|
||||
|
||||
> Vault provider is only available in Grafana Enterprise v7.1+. For more information, refer to [Vault integration]({{< relref "../configure-security/configure-database-encryption/integrate-with-hashicorp-vault/" >}}) in [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
> Vault provider is only available in Grafana Enterprise v7.1+. For more information, refer to [Vault integration]({{< relref "../configure-security/configure-database-encryption/integrate-with-hashicorp-vault/" >}}) in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}).
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -369,7 +369,7 @@ Defaults to `private`.
|
||||
|
||||
## [remote_cache]
|
||||
|
||||
Caches authentication details and session information in the configured database, Redis or Memcached. This setting does not configure [Query Caching in Grafana Enterprise]({{< relref "../../enterprise/query-caching/" >}}).
|
||||
Caches authentication details and session information in the configured database, Redis or Memcached. This setting does not configure [Query Caching in Grafana Enterprise]({{< relref "../../administration/data-source-management/#query-caching" >}}).
|
||||
|
||||
### type
|
||||
|
||||
@@ -534,6 +534,10 @@ Default is `admin`.
|
||||
|
||||
The password of the default Grafana Admin. Set once on first-run. Default is `admin`.
|
||||
|
||||
# admin_email
|
||||
|
||||
The email of the default Grafana Admin, created on startup. Default is `admin@localhost`.
|
||||
|
||||
### secret_key
|
||||
|
||||
Used for signing some data source settings like secrets and passwords, the encryption format used is AES-256 in CFB mode. Cannot be changed without requiring an update
|
||||
@@ -1951,7 +1955,7 @@ Change the listening port of the gRPC server. Default port is `0` and will autom
|
||||
|
||||
## [enterprise]
|
||||
|
||||
For more information about Grafana Enterprise, refer to [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
For more information about Grafana Enterprise, refer to [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}).
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ Setting 'enabled' to `true` allows users to configure query caching for data sou
|
||||
|
||||
This value is `true` by default.
|
||||
|
||||
> **Note:** This setting enables the caching feature, but it does not turn on query caching for any data source. To turn on query caching for a data source, update the setting on the data source configuration page. For more information, refer to the [query caching docs]({{< relref "../../enterprise/query-caching/#enable-and-configure-query-caching" >}}).
|
||||
> **Note:** This setting enables the caching feature, but it does not turn on query caching for any data source. To turn on query caching for a data source, update the setting on the data source configuration page. For more information, refer to the [Query caching]({{< relref "../../../administration/data-source-management/#enable-and-configure-query-caching" >}}).
|
||||
|
||||
### ttl
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ weight: 500
|
||||
By updating settings at runtime, you can update Grafana settings without needing to restart the Grafana server.
|
||||
|
||||
Updates that happen at runtime are stored in the database and override
|
||||
[settings from the other sources]({{< relref "../setup-grafana/configure-grafana/" >}})
|
||||
[settings from the other sources]({{< relref "../../configure-grafana/" >}})
|
||||
(arguments, environment variables, settings file, etc). Therefore, every time a specific setting key is removed at runtime,
|
||||
the value used for that key is the inherited one from the other sources in the reverse order of precedence
|
||||
(`arguments > environment variables > settings file`), being the application default the value used when no one provided
|
||||
@@ -27,7 +27,7 @@ Currently, **it only supports updates on the `auth.saml` section.**
|
||||
|
||||
## Update settings via the API
|
||||
|
||||
You can update settings through the [Admin API]({{< relref "../developers/http_api/admin/#update-settings" >}}).
|
||||
You can update settings through the [Admin API]({{< relref "../../../developers/http_api/admin/#update-settings" >}}).
|
||||
|
||||
When you submit a settings update via API, Grafana verifies if the given settings updates are allowed and valid. If they are, then Grafana stores the settings in the database and reloads
|
||||
Grafana services with no need to restart the instance.
|
||||
@@ -92,5 +92,5 @@ HTTP API, then the other instances are synchronized through the database and the
|
||||
|
||||
## Control access with role-based access control
|
||||
|
||||
If you have [role-based access control]({{< relref "../administration/roles-and-permissions/access-control/" >}}) enabled, you can control who can read or update settings.
|
||||
Refer to the [Admin API]({{< relref "../developers/http_api/admin/#update-settings" >}}) for more information.
|
||||
If you have [role-based access control]({{< relref "../../../administration/roles-and-permissions/access-control/" >}}) enabled, you can control who can read or update settings.
|
||||
Refer to the [Admin API]({{< relref "../../../developers/http_api/admin/#update-settings" >}}) for more information.
|
||||
@@ -16,7 +16,9 @@ weight: 800
|
||||
|
||||
Auditing allows you to track important changes to your Grafana instance. By default, audit logs are logged to file but the auditing feature also supports sending logs directly to Loki.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
Only API requests or UI actions that trigger an API request generate an audit log.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Advanced](/docs/grafana-cloud).
|
||||
|
||||
## Audit logs
|
||||
|
||||
|
||||
@@ -50,19 +50,19 @@ You can re-encrypt secrets in order to:
|
||||
- Move already existing secrets' encryption forward from legacy to envelope encryption.
|
||||
- Re-encrypt secrets after a [data keys rotation](#rotate-data-keys).
|
||||
|
||||
To re-encrypt secrets, use the [Grafana CLI]({{< ref "/cli/" >}}) by running the `grafana-cli admin secrets-migration re-encrypt` command or the `/encryption/reencrypt-secrets` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#roll-back-secrets" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
To re-encrypt secrets, use the [Grafana CLI]({{< ref "../../../cli" >}}) by running the `grafana-cli admin secrets-migration re-encrypt` command or the `/encryption/reencrypt-secrets` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#roll-back-secrets" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
|
||||
### Roll back secrets
|
||||
|
||||
You can roll back secrets encrypted with envelope encryption to legacy encryption. This might be necessary to downgrade to Grafana versions prior to v9.0 after an unsuccessful upgrade.
|
||||
|
||||
To roll back secrets, use the [Grafana CLI]({{< ref "/cli/" >}}) by running the `grafana-cli admin secrets-migration rollback` command or the `/encryption/rollback-secrets` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#re-encrypt-secrets" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
To roll back secrets, use the [Grafana CLI]({{< ref "../../../cli" >}}) by running the `grafana-cli admin secrets-migration rollback` command or the `/encryption/rollback-secrets` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#re-encrypt-secrets" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
|
||||
### Re-encrypt data keys
|
||||
|
||||
You can re-encrypt data keys encrypted with a specific key encryption key (KEK). This allows you to either re-encrypt existing data keys with a new KEK version (see [KMS integration](#kms-integration) rotation) or to re-encrypt them with a completely different KEK.
|
||||
|
||||
To re-encrypt data keys, use the [Grafana CLI]({{< ref "/cli/" >}}) by running the `grafana-cli admin secrets-migration re-encrypt-data-keys` command or the `/encryption/reencrypt-data-keys` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#re-encrypt-data-encryption-keys" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
To re-encrypt data keys, use the [Grafana CLI]({{< ref "../../../cli" >}}) by running the `grafana-cli admin secrets-migration re-encrypt-data-keys` command or the `/encryption/reencrypt-data-keys` endpoint of the Grafana [Admin API]({{< relref "../../../developers/http_api/admin/#re-encrypt-data-encryption-keys" >}}). It's safe to run more than once, more recommended under maintenance mode.
|
||||
|
||||
### Rotate data keys
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Request security allows you to limit requests from the Grafana server by targeti
|
||||
|
||||
This can be used to limit access to internal systems that the server Grafana runs on can access but that users of Grafana should not be able to access. This feature does not affect traffic from the Grafana users browser.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
> **Note:** Although request security works with backend plugins, you can create a backend plugin that bypasses this security.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ weight: 1000
|
||||
|
||||
Team sync lets you set up synchronization between your auth providers teams and teams in Grafana. This enables LDAP, OAuth, or SAML users who are members of certain teams or groups to automatically be added or removed as members of certain teams in Grafana.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../enterprise/" >}}) and [Grafana Cloud Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Advanced](/docs/grafana-cloud/).
|
||||
|
||||
Grafana keeps track of all synchronized users in teams, and you can see which users have been synchronized in the team members list, see `LDAP` label in screenshot.
|
||||
This mechanism allows Grafana to remove an existing synchronized user from a team when its group membership changes. This mechanism also enables you to manually add a user as member of a team, and it will not be removed when the user signs in. This gives you flexibility to combine LDAP group memberships and Grafana team memberships.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight: 1000
|
||||
|
||||
# Set up image rendering
|
||||
|
||||
Grafana supports automatic rendering of panels as PNG images. This allows Grafana to automatically generate images of your panels to include in [alert notifications]({{< relref "../../alerting/notifications/" >}}), [PDF export]({{< relref "../../dashboards/create-reports/#export-dashboard-as-pdf" >}}), and [Reporting]({{< relref "../../dashboards/create-reports/" >}}). PDF Export and Reporting are available only in [Grafana Enterprise]({{< relref "../../enterprise/" >}}).
|
||||
Grafana supports automatic rendering of panels as PNG images. This allows Grafana to automatically generate images of your panels to include in alert notifications, [PDF export]({{< relref "../../dashboards/create-reports/#export-dashboard-as-pdf" >}}), and [Reporting]({{< relref "../../dashboards/create-reports/" >}}). PDF Export and Reporting are available only in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}).
|
||||
|
||||
> **Note:** Image rendering of dashboards is not supported at this time.
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ This page explains how to install Grafana dependencies, download and install Gra
|
||||
|
||||
While the process for upgrading Grafana is very similar to installing Grafana, there are some key backup steps you should perform. Read [Upgrading Grafana]({{< relref "../upgrade-grafana/" >}}) for tips and guidance on updating an existing installation.
|
||||
|
||||
> **Note:** You can use [Grafana Cloud](https://grafana.com/products/cloud/features/#cloud-logs) to avoid the overhead of installing, maintaining, and scaling your observability stack. The free forever plan includes Grafana, 10K Prometheus series, 50 GB logs, and more.[Create a free account to get started](https://grafana.com/auth/sign-up/create-user?pg=docs-grafana-install&plcmt=in-text).
|
||||
|
||||
## 1. Download and install
|
||||
|
||||
You can install Grafana using our official APT repository, by downloading a `.deb` package, or by downloading a binary `.tar.gz` file.
|
||||
|
||||
@@ -17,8 +17,6 @@ This topic explains how to install Grafana dependencies, download and install Gr
|
||||
|
||||
While the process for upgrading Grafana is very similar to installing Grafana, there are some key backup steps you should perform. Read [Upgrading Grafana]({{< relref "../upgrade-grafana/" >}}) for tips and guidance on updating an existing installation.
|
||||
|
||||
> **Note:** You can use [Grafana Cloud](https://grafana.com/products/cloud/features/#cloud-logs) to avoid the overhead of installing, maintaining, and scaling your observability stack. The free forever plan includes Grafana, 10K Prometheus series, 50 GB logs, and more.[Create a free account to get started](https://grafana.com/auth/sign-up/create-user?pg=docs-grafana-install&plcmt=in-text).
|
||||
|
||||
## 1. Download and install
|
||||
|
||||
You can install Grafana from a YUM repository, manually using YUM, manually using RPM, or by downloading a binary `.tar.gz` file.
|
||||
|
||||
@@ -404,3 +404,7 @@ The concept of a `default` data source existed in Grafana since the beginning. H
|
||||
This behavior was not very intuitive and creates issues for users who want to change the default without it impacting existing dashboards.
|
||||
That is why we are changing the behavior in 8.5. From now on, the `default` data source will not be a persisted property but just the starting data source for new panels and queries.
|
||||
Existing dashboards that still have panels with a `datasource` set to null will be migrated when the dashboard opens. The migration will set the data source property to the **current** default data source.
|
||||
|
||||
## Upgrading to v9.2
|
||||
|
||||
Beginning in v9.2, Grafana has a [supported database versions policy]({{< relref "./installation/#supported-databases" >}}). As of this release, MySQL versions from 5.7, postgres versions from v10, and SQLite 3 are supported databases.
|
||||
|
||||
@@ -7,5 +7,3 @@ title: Grafana Cloud
|
||||
## Grafana Cloud
|
||||
|
||||
Grafana Cloud is a highly available, fast, fully managed OpenSaaS logging and metrics platform. It is everything you love about Grafana, but Grafana Labs hosts it for you and handles all the headaches.
|
||||
|
||||
[Learn more about Grafana Cloud](https://grafana.com/cloud/) and get started with your [free account with Grafana Cloud](https://grafana.com/signup/cloud/connect-account?pg=gsdocs) that includes a robust free tier with access to 10k metrics, 50GB logs, 50GB traces, 2 weeks data retention and 3 users.
|
||||
|
||||
@@ -123,7 +123,7 @@ Query caching advantages:
|
||||
|
||||
Caching currently works for all backend data sources. You can enable the cache globally or per data source, and you can configure the cache duration per data source. The cache is currently in-memory.
|
||||
|
||||
For more information, refer to [Query caching]({{< relref "../enterprise/query-caching/" >}}).
|
||||
For more information, refer to [Query caching]({{< relref "../administration/data-source-management/#query-caching" >}}).
|
||||
|
||||
### Use template variable in reports
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ This results in faster average load times for dashboards and fewer duplicate que
|
||||
|
||||
You can enable caching per data source, and time-to-live (TTL) can be configured globally and per data source. Query caching can be set up with Redis, Memcached, or a simple in-memory cache.
|
||||
|
||||
For more information, refer to the [Data source query caching docs]({{< relref "../enterprise/query-caching/" >}}).
|
||||
For more information, refer to the [Data source query caching docs]({{< relref "../administration/data-source-management/#query-caching" >}}).
|
||||
|
||||
### Reporting updates
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ We’ve enhanced the scheduler for Reports to be more flexible, so you can send
|
||||
|
||||
### Encrypt data in the query cache
|
||||
|
||||
Query caching was released in Grafana 8.0 and allows you to temporarily store the results of data source queries in a cache, so that Grafana reads repeated queries from there instead of from the data source itself. This reduces load on data sources, improves dashboard load times, and can save money for data sources that charge per query. To learn more about query caching see its [overview]({{< relref "../enterprise/query-caching/" >}}) page. To find out how to turn on encryption, refer to the [caching configuration]({{< relref "../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}) documentation.
|
||||
Query caching was released in Grafana 8.0 and allows you to temporarily store the results of data source queries in a cache, so that Grafana reads repeated queries from there instead of from the data source itself. This reduces load on data sources, improves dashboard load times, and can save money for data sources that charge per query. To learn more about query caching see its [overview]({{< relref "../administration/data-source-management/#query-caching" >}}) page. To find out how to turn on encryption, refer to the [caching configuration]({{< relref "../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}) documentation.
|
||||
|
||||
You can now encrypt the query data cached by Grafana. This improves the security of query data, especially when your cache (like Redis) is shared with other services.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ All functions, aggregations and binary operations are added via the + Operation
|
||||
|
||||
### Range vector
|
||||
|
||||
The query builder will automatically mange and add the range selector. It will be shown as a parameter to the operations that require a range vector (rate, delta, increase, etc).
|
||||
The query builder will automatically manage and add the range selector. It will be shown as a parameter to the operations that require a range vector (rate, delta, increase, etc).
|
||||
|
||||
### Binary operations
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Service accounts are an evolution in machine access within Grafana.
|
||||
You can create multiple API tokens per service account with independent expiration dates, and temporarily disable a service account without deleting it.
|
||||
These benefits make service accounts a more flexible way for Terraform and other apps to authenticate with Grafana.
|
||||
|
||||
Service accounts also work with [role-based access control]({{< relref "../administration/roles-and-permissions/access-control" >}}) in [Grafana Enterprise]({{< relref "../enterprise/" >}}).
|
||||
Service accounts also work with [role-based access control]({{< relref "../administration/roles-and-permissions/access-control" >}}) in [Grafana Enterprise]({{< relref "../introduction/grafana-enterprise" >}}).
|
||||
You can improve security by granting service accounts specific roles to limit the functions they can perform.
|
||||
Service accounts have been in beta since Grafana v8.5.
|
||||
During that time, we've improved the UI and migration path from API keys, made it possible to add service accounts to teams, and inherit team permissions.
|
||||
|
||||
@@ -219,3 +219,19 @@ For more information, see the [SAML configuration documentation](https://grafana
|
||||
You can now map OAuth groups and roles to Server Admin for the GitLab, GitHub, AzureAD, Okta, and Generic OAuth integrations.
|
||||
To enable this functionality, set the `allow_assign_grafana_admin` configuration option to `true` in the desired OAuth integration section.
|
||||
For more information, see the [authentication configuration documentation](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/) for each OAuth client.
|
||||
|
||||
## Match parameter support in prometheus labels API
|
||||
|
||||
Prometheus users running Prometheus v2.24 and higher can use the [labels endpoint](https://prometheus.io/docs/prometheus/latest/querying/api/#querying-label-values) instead of the [series endpoint](https://prometheus.io/docs/prometheus/latest/querying/api/#finding-series-by-label-matchers) for the [`label_values` function]({{< relref "../datasources/prometheus/#query-variable" >}}).
|
||||
This decreases load times for templated high-cardinality Prometheus instances.
|
||||
|
||||
If you want to benefit from this endpoint you must first configure the Prometheus type and version in any Prometheus data sources' [configuration]({{< relref "../datasources/prometheus/" >}}).
|
||||
|
||||
## New Prometheus streaming parser
|
||||
|
||||
In Grafana v9.2, you can enable the `prometheusStreamingJSONParser` [feature toggle]({{< relref "../setup-grafana/configure-grafana/#feature_toggles" >}}) to use a better-performing, memory-efficient streaming JSON client for Prometheus.
|
||||
We'll make this client the default in Grafana v9.3.
|
||||
|
||||
When Prometheus returns `NaN` values, this new client doesn't change them, neither to the value `null` nor to `0` as in recent Grafana versions.
|
||||
If you use this new Prometheus streaming parser with Grafana Managed Alerts, this change in behavior might trigger alerts.
|
||||
To avoid this, select the "Drop non-numeric values" option in the Reduce expression to drop `NaN` values.
|
||||
|
||||
@@ -16,7 +16,7 @@ e2e.scenario({
|
||||
itName: 'Tests dashboard time zone scenarios',
|
||||
addScenarioDataSource: false,
|
||||
addScenarioDashBoard: false,
|
||||
skipScenario: false,
|
||||
skipScenario: true,
|
||||
scenario: () => {
|
||||
e2e.flows.openDashboard({ uid: '5SdHCasdf' });
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ const addDataSource = () => {
|
||||
e2e.components.DataSource.Prometheus.configPage.exemplarsAddButton().click();
|
||||
e2e.components.DataSource.Prometheus.configPage.internalLinkSwitch().check({ force: true });
|
||||
e2e.components.DataSource.DataSourceHttpSettings.urlInput().type('http://prom-url:9090');
|
||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').click({ force: true });
|
||||
e2e.components.DataSourcePicker.inputV2().click({ force: true }).should('have.focus');
|
||||
|
||||
e2e().contains('gdev-tempo').scrollIntoView().should('be.visible').click();
|
||||
},
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
// Override xorm's outdated go-mssqldb dependency, since we can't upgrade to current xorm (due to breaking changes).
|
||||
// We need a more current go-mssqldb so we get rid of a version of apache/thrift with vulnerabilities.
|
||||
@@ -230,7 +230,7 @@ require (
|
||||
go.uber.org/atomic v1.9.0
|
||||
go.uber.org/goleak v1.1.12 // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/text v0.4.0
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3
|
||||
|
||||
2
go.sum
2
go.sum
@@ -3089,6 +3089,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "9.2.2"
|
||||
"version": "9.2.4"
|
||||
}
|
||||
|
||||
14
package.json
14
package.json
@@ -3,7 +3,7 @@
|
||||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
|
||||
@@ -190,7 +190,6 @@
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-webpack-plugin": "3.2.0",
|
||||
"expose-loader": "4.0.0",
|
||||
"file-loader": "6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "7.2.13",
|
||||
"fs-extra": "10.1.0",
|
||||
"glob": "8.0.3",
|
||||
@@ -217,7 +216,6 @@
|
||||
"postcss-reporter": "7.0.5",
|
||||
"postcss-scss": "4.0.4",
|
||||
"prettier": "2.7.1",
|
||||
"raw-loader": "4.0.2",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-select-event": "5.5.0",
|
||||
"react-simple-compat": "1.2.3",
|
||||
@@ -406,11 +404,21 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"underscore": "1.13.4",
|
||||
"@mdx-js/loader/loader-utils": "^2.0.0",
|
||||
"@types/slate": "0.47.9",
|
||||
"@rushstack/node-core-library": "3.52.0",
|
||||
"@rushstack/rig-package": "0.3.13",
|
||||
"@rushstack/ts-command-line": "4.12.1",
|
||||
"@storybook/builder-webpack4/css-loader": "6.7.1",
|
||||
"@storybook/builder-webpack4/html-webpack-plugin": "5.5.0",
|
||||
"@storybook/builder-webpack4/webpack": "5.74.0",
|
||||
"@storybook/core-common/webpack": "5.74.0",
|
||||
"@storybook/core-server/webpack": "5.74.0",
|
||||
"@storybook/manager-webpack4/css-loader": "6.7.1",
|
||||
"@storybook/manager-webpack4/html-webpack-plugin": "5.5.0",
|
||||
"@storybook/manager-webpack4/webpack": "5.74.0",
|
||||
"@storybook/react/webpack": "5.74.0",
|
||||
"ngtemplate-loader/loader-utils": "^2.0.0",
|
||||
"node-fetch": "2.6.7",
|
||||
"slate-dev-environment@^0.2.2": "patch:slate-dev-environment@npm:0.2.5#.yarn/patches/slate-dev-environment-npm-0.2.5-9aeb7da7b5.patch"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.0",
|
||||
"@grafana/schema": "9.2.2",
|
||||
"@grafana/schema": "9.2.4",
|
||||
"@types/d3-interpolate": "^1.4.0",
|
||||
"d3-interpolate": "1.4.0",
|
||||
"date-fns": "2.29.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana End-to-End Test Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -61,7 +61,7 @@
|
||||
"@babel/core": "7.19.0",
|
||||
"@babel/preset-env": "7.19.0",
|
||||
"@cypress/webpack-preprocessor": "5.12.0",
|
||||
"@grafana/e2e-selectors": "9.2.2",
|
||||
"@grafana/e2e-selectors": "9.2.4",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"babel-loader": "8.2.5",
|
||||
@@ -77,7 +77,7 @@
|
||||
"resolve-as-bin": "2.1.0",
|
||||
"rimraf": "3.0.2",
|
||||
"tracelib": "1.0.1",
|
||||
"ts-loader": "6.2.2",
|
||||
"ts-loader": "8.4.0",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.8.2",
|
||||
"uuid": "8.3.2",
|
||||
|
||||
@@ -90,7 +90,13 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC
|
||||
e2e.components.Panels.Panel.title(panelTitle).click();
|
||||
e2e.components.Panels.Panel.headerItems('Edit').click();
|
||||
} else {
|
||||
e2e.components.PageToolbar.item('Add panel').click();
|
||||
try {
|
||||
e2e.components.PageToolbar.item('Add panel').click();
|
||||
} catch (e) {
|
||||
// Depending on the screen size, the "Add panel" button might be hidden
|
||||
e2e.components.PageToolbar.item('Show more items').click();
|
||||
e2e.components.PageToolbar.item('Add panel').last().click();
|
||||
}
|
||||
e2e.pages.AddDashboard.addNewPanel().click();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -36,9 +36,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/agent-web": "^0.4.0",
|
||||
"@grafana/data": "9.2.2",
|
||||
"@grafana/e2e-selectors": "9.2.2",
|
||||
"@grafana/ui": "9.2.2",
|
||||
"@grafana/data": "9.2.4",
|
||||
"@grafana/e2e-selectors": "9.2.4",
|
||||
"@grafana/ui": "9.2.4",
|
||||
"@sentry/browser": "6.19.7",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/toolkit",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana Toolkit",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -50,10 +50,10 @@
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@grafana/data": "9.2.2",
|
||||
"@grafana/data": "9.2.4",
|
||||
"@grafana/eslint-config": "5.0.0",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@grafana/ui": "9.2.2",
|
||||
"@grafana/ui": "9.2.4",
|
||||
"@jest/core": "27.5.1",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/eslint": "8.4.1",
|
||||
@@ -85,7 +85,6 @@
|
||||
"eslint-plugin-react": "7.29.4",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"execa": "^5.1.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"globby": "^11.0.4",
|
||||
@@ -122,7 +121,6 @@
|
||||
"ts-node": "^9.1.0",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.8.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.72.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,9 @@ export const getStyleLoaders = () => {
|
||||
{
|
||||
loader: require.resolve('less-loader'),
|
||||
options: {
|
||||
javascriptEnabled: true,
|
||||
lessOptions: {
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -47,9 +47,9 @@
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.9.0",
|
||||
"@emotion/react": "11.9.3",
|
||||
"@grafana/data": "9.2.2",
|
||||
"@grafana/e2e-selectors": "9.2.2",
|
||||
"@grafana/schema": "9.2.2",
|
||||
"@grafana/data": "9.2.4",
|
||||
"@grafana/e2e-selectors": "9.2.4",
|
||||
"@grafana/schema": "9.2.4",
|
||||
"@monaco-editor/react": "4.4.5",
|
||||
"@popperjs/core": "2.11.5",
|
||||
"@react-aria/button": "3.6.1",
|
||||
@@ -178,7 +178,6 @@
|
||||
"process": "^0.11.10",
|
||||
"raw-loader": "4.0.2",
|
||||
"react": "17.0.2",
|
||||
"react-docgen-typescript-loader": "3.7.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"rimraf": "3.0.2",
|
||||
|
||||
@@ -129,6 +129,7 @@ Object {
|
||||
],
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
@@ -152,6 +153,7 @@ Object {
|
||||
],
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
@@ -175,6 +177,7 @@ Object {
|
||||
],
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
@@ -198,6 +201,7 @@ Object {
|
||||
],
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
@@ -221,6 +225,7 @@ Object {
|
||||
],
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
|
||||
option: () => ({}),
|
||||
placeholder: (originalStyles: CSSObjectWithLabel) => ({
|
||||
...originalStyles,
|
||||
color: theme.colors.text.disabled,
|
||||
color: theme.colors.text.secondary,
|
||||
}),
|
||||
singleValue: () => ({}),
|
||||
valueContainer: () => ({}),
|
||||
|
||||
275
packages/grafana-ui/src/components/TimeSeries/utils.test.ts
Normal file
275
packages/grafana-ui/src/components/TimeSeries/utils.test.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import { ArrayVector, FieldType } from '@grafana/data';
|
||||
import { getTheme } from '@grafana/ui';
|
||||
|
||||
import { preparePlotConfigBuilder } from './utils';
|
||||
|
||||
describe('when fill below to option is used', () => {
|
||||
// eslint-disable-next-line
|
||||
let eventBus: any;
|
||||
// eslint-disable-next-line
|
||||
let renderers: any[];
|
||||
// eslint-disable-next-line
|
||||
let tests: any;
|
||||
|
||||
beforeEach(() => {
|
||||
eventBus = {
|
||||
publish: jest.fn(),
|
||||
getStream: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
removeAllListeners: jest.fn(),
|
||||
newScopedBus: jest.fn(),
|
||||
};
|
||||
renderers = [];
|
||||
|
||||
tests = [
|
||||
{
|
||||
alignedFrame: {
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test1', custom: { fillBelowTo: 'Test2' }, min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test2', min: 0, max: 100 },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test2', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
allFrames: [
|
||||
{
|
||||
name: 'Test1',
|
||||
refId: 'A',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test1', custom: { fillBelowTo: 'Test2' }, min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 2,
|
||||
},
|
||||
{
|
||||
name: 'Test2',
|
||||
refId: 'B',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 1 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test2', min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test2', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 2,
|
||||
},
|
||||
],
|
||||
expectedResult: 1,
|
||||
},
|
||||
{
|
||||
alignedFrame: {
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value1' } },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'value1',
|
||||
state: { multipleFrames: true, displayName: 'value1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value2' } },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'value2',
|
||||
state: { multipleFrames: true, displayName: 'value2', origin: { fieldIndex: 2, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value1',
|
||||
state: { multipleFrames: true, displayName: 'below_value1', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value2',
|
||||
state: { multipleFrames: true, displayName: 'below_value2', origin: { fieldIndex: 2, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 5,
|
||||
},
|
||||
allFrames: [
|
||||
{
|
||||
refId: 'A',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value1' } },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'value1',
|
||||
state: { multipleFrames: true, displayName: 'value1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value2' } },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'value2',
|
||||
state: { multipleFrames: true, displayName: 'value2', origin: { fieldIndex: 2, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
{
|
||||
refId: 'B',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 1 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value1',
|
||||
state: { multipleFrames: true, displayName: 'below_value1', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value2',
|
||||
state: { multipleFrames: true, displayName: 'below_value2', origin: { fieldIndex: 2, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
expectedResult: 2,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
it('should verify if fill below to is set then builder bands are set', () => {
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
|
||||
it('should verify if fill below to is not set then builder bands are empty', () => {
|
||||
tests[0].alignedFrame.fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[0].allFrames[0].fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].alignedFrame.fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].alignedFrame.fields[2].config.custom.fillBelowTo = undefined;
|
||||
tests[1].allFrames[0].fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].allFrames[0].fields[2].config.custom.fillBelowTo = undefined;
|
||||
tests[0].expectedResult = 0;
|
||||
tests[1].expectedResult = 0;
|
||||
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
|
||||
it('should verify if fill below to is set and field name is overriden then builder bands are set', () => {
|
||||
tests[0].alignedFrame.fields[2].config.displayName = 'newName';
|
||||
tests[0].alignedFrame.fields[2].state.displayName = 'newName';
|
||||
tests[0].allFrames[1].fields[1].config.displayName = 'newName';
|
||||
tests[0].allFrames[1].fields[1].state.displayName = 'newName';
|
||||
|
||||
tests[1].alignedFrame.fields[3].config.displayName = 'newName';
|
||||
tests[1].alignedFrame.fields[3].state.displayName = 'newName';
|
||||
tests[1].allFrames[1].fields[1].config.displayName = 'newName';
|
||||
tests[1].allFrames[1].fields[1].state.displayName = 'newName';
|
||||
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -296,7 +296,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
|
||||
let pointsFilter: uPlot.Series.Points.Filter = () => null;
|
||||
|
||||
if (customConfig.spanNulls !== true) {
|
||||
if (customConfig.spanNulls !== true && showPoints !== VisibilityMode.Always) {
|
||||
pointsFilter = (u, seriesIdx, show, gaps) => {
|
||||
let filtered = [];
|
||||
|
||||
@@ -400,8 +400,19 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
}
|
||||
|
||||
if (customConfig.fillBelowTo) {
|
||||
const fillBelowToField = frame.fields.find(
|
||||
(f) =>
|
||||
customConfig.fillBelowTo === f.name ||
|
||||
customConfig.fillBelowTo === f.config?.displayNameFromDS ||
|
||||
customConfig.fillBelowTo === getFieldDisplayName(f, frame, allFrames)
|
||||
);
|
||||
|
||||
const fillBelowDispName = fillBelowToField
|
||||
? getFieldDisplayName(fillBelowToField, frame, allFrames)
|
||||
: customConfig.fillBelowTo;
|
||||
|
||||
const t = indexByName.get(dispName);
|
||||
const b = indexByName.get(customConfig.fillBelowTo);
|
||||
const b = indexByName.get(fillBelowDispName);
|
||||
if (isNumber(b) && isNumber(t)) {
|
||||
builder.addBand({
|
||||
series: [t, b],
|
||||
@@ -423,7 +434,40 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
dynamicSeriesColor = (seriesIdx) => getFieldSeriesColor(alignedFrame.fields[seriesIdx], theme).color;
|
||||
}
|
||||
|
||||
// this adds leading and trailing gaps when datasets have leading and trailing nulls
|
||||
// it will cause additional unnecessary clips, but we also use adjacent gaps to show single points
|
||||
// when not connecting across gaps, e.g. null,100,null,null,50,50,50,null,50,null,null
|
||||
const gapsRefiner: uPlot.Series.GapsRefiner = (u, seriesIdx, idx0, idx1, gaps) => {
|
||||
let yData = u.data[seriesIdx];
|
||||
|
||||
// @ts-ignore
|
||||
let xData = u._data[0];
|
||||
|
||||
// scan to first and last non-null vals
|
||||
let first = idx0,
|
||||
last = idx1;
|
||||
|
||||
while (first <= last && yData[first] == null) {
|
||||
first++;
|
||||
}
|
||||
|
||||
while (last > first && yData[last] == null) {
|
||||
last--;
|
||||
}
|
||||
|
||||
if (first !== idx0) {
|
||||
gaps.unshift([u.bbox.left, Math.round(u.valToPos(xData[first]!, 'x', true))]);
|
||||
}
|
||||
|
||||
if (last !== idx1) {
|
||||
gaps.push([Math.round(u.valToPos(xData[last]!, 'x', true)), u.bbox.left + u.bbox.width]);
|
||||
}
|
||||
|
||||
return gaps;
|
||||
};
|
||||
|
||||
builder.addSeries({
|
||||
gapsRefiner,
|
||||
pathBuilder,
|
||||
pointsBuilder,
|
||||
scaleKey,
|
||||
|
||||
@@ -613,6 +613,7 @@ describe('UPlotConfigBuilder', () => {
|
||||
Object {
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#0000ff",
|
||||
@@ -740,6 +741,7 @@ describe('UPlotConfigBuilder', () => {
|
||||
Object {
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#0000ff",
|
||||
@@ -758,6 +760,7 @@ describe('UPlotConfigBuilder', () => {
|
||||
Object {
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#00ff00",
|
||||
@@ -776,6 +779,7 @@ describe('UPlotConfigBuilder', () => {
|
||||
Object {
|
||||
"facets": undefined,
|
||||
"fill": [Function],
|
||||
"gaps": [Function],
|
||||
"paths": [Function],
|
||||
"points": Object {
|
||||
"fill": "#ff0000",
|
||||
|
||||
@@ -49,6 +49,8 @@ export interface SeriesProps extends LineConfig, BarConfig, FillConfig, PointsCo
|
||||
dataFrameFieldIndex?: DataFrameFieldIndex;
|
||||
theme: GrafanaTheme2;
|
||||
value?: uPlot.Series.Value;
|
||||
|
||||
gapsRefiner?: uPlot.Series.GapsRefiner;
|
||||
}
|
||||
|
||||
export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
@@ -71,6 +73,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
pxAlign,
|
||||
spanNulls,
|
||||
show = true,
|
||||
gapsRefiner,
|
||||
} = this.props;
|
||||
|
||||
let lineConfig: Partial<Series> = {};
|
||||
@@ -145,6 +148,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
pxAlign,
|
||||
show,
|
||||
fill: this.getFill(),
|
||||
gaps: gapsRefiner ?? ((u, seriesIdx, idx0, idx1, gaps) => gaps),
|
||||
...lineConfig,
|
||||
...pointsConfig,
|
||||
};
|
||||
|
||||
@@ -1063,7 +1063,7 @@ describe('auto stacking groups', () => {
|
||||
},
|
||||
{
|
||||
name: 'd',
|
||||
values: [-0, -10, -20],
|
||||
values: [null, -0, null],
|
||||
config: { custom: { stacking: { mode: StackingMode.Normal } } },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -116,8 +116,7 @@ export function getStackingGroups(frame: DataFrame) {
|
||||
// will this be stacked up or down after any transforms applied
|
||||
let vals = values.toArray();
|
||||
let transform = custom.transform;
|
||||
let firstValue = vals.find((v) => v != null);
|
||||
let stackDir = getStackDirection(transform, firstValue);
|
||||
let stackDir = getStackDirection(transform, vals);
|
||||
|
||||
let drawStyle = custom.drawStyle as GraphDrawStyle;
|
||||
let drawStyle2 =
|
||||
@@ -341,13 +340,48 @@ export function findMidPointYPosition(u: uPlot, idx: number) {
|
||||
return y;
|
||||
}
|
||||
|
||||
function getStackDirection(transform: GraphTransform, firstValue: number) {
|
||||
// Check if first value is negative zero. This can happen with a binary operation transform.
|
||||
const isNegativeZero = Object.is(firstValue, -0);
|
||||
function getStackDirection(transform: GraphTransform, data: unknown[]) {
|
||||
const hasNegSamp = hasNegSample(data);
|
||||
|
||||
if (transform === GraphTransform.NegativeY) {
|
||||
return !isNegativeZero && firstValue >= 0 ? StackDirection.Neg : StackDirection.Pos;
|
||||
return hasNegSamp ? StackDirection.Pos : StackDirection.Neg;
|
||||
}
|
||||
return !isNegativeZero && firstValue >= 0 ? StackDirection.Pos : StackDirection.Neg;
|
||||
return hasNegSamp ? StackDirection.Neg : StackDirection.Pos;
|
||||
}
|
||||
|
||||
// similar to isLikelyAscendingVector()
|
||||
function hasNegSample(data: unknown[], samples = 50) {
|
||||
const len = data.length;
|
||||
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip leading & trailing nullish
|
||||
let firstIdx = 0;
|
||||
let lastIdx = len - 1;
|
||||
|
||||
while (firstIdx <= lastIdx && data[firstIdx] == null) {
|
||||
firstIdx++;
|
||||
}
|
||||
|
||||
while (lastIdx >= firstIdx && data[lastIdx] == null) {
|
||||
lastIdx--;
|
||||
}
|
||||
|
||||
if (lastIdx >= firstIdx) {
|
||||
const stride = Math.max(1, Math.floor((lastIdx - firstIdx + 1) / samples));
|
||||
|
||||
for (let i = firstIdx; i <= lastIdx; i += stride) {
|
||||
const v = data[i];
|
||||
|
||||
if (v != null && (v < 0 || Object.is(v, -0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dev helpers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jaegertracing/jaeger-ui-components",
|
||||
"version": "9.2.2",
|
||||
"version": "9.2.4",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,10 +31,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.9.0",
|
||||
"@grafana/data": "9.2.2",
|
||||
"@grafana/e2e-selectors": "9.2.2",
|
||||
"@grafana/runtime": "9.2.2",
|
||||
"@grafana/ui": "9.2.2",
|
||||
"@grafana/data": "9.2.4",
|
||||
"@grafana/e2e-selectors": "9.2.4",
|
||||
"@grafana/runtime": "9.2.4",
|
||||
"@grafana/ui": "9.2.4",
|
||||
"chance": "^1.0.10",
|
||||
"classnames": "^2.2.5",
|
||||
"combokeys": "^3.0.0",
|
||||
|
||||
@@ -26,10 +26,8 @@ ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bi
|
||||
|
||||
WORKDIR $GF_PATHS_HOME
|
||||
|
||||
RUN apk add --no-cache ca-certificates bash tzdata && \
|
||||
apk add --no-cache musl-utils
|
||||
|
||||
RUN apk add --no-cache openssl --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
|
||||
RUN apk add --no-cache ca-certificates bash tzdata musl-utils
|
||||
RUN apk info -vv | sort
|
||||
|
||||
# Oracle Support for x86_64 only
|
||||
RUN if [ `arch` = "x86_64" ]; then \
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -37,6 +38,10 @@ func (hs *HTTPServer) AdminCreateUser(c *models.ReqContext) response.Response {
|
||||
if err := web.Bind(c.Req, &form); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
form.Email = strings.TrimSpace(form.Email)
|
||||
form.Login = strings.TrimSpace(form.Login)
|
||||
|
||||
cmd := user.CreateUserCommand{
|
||||
Login: form.Login,
|
||||
Email: form.Email,
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -217,21 +218,37 @@ func (hs *HTTPServer) GetInviteInfoByCode(c *models.ReqContext) response.Respons
|
||||
|
||||
func (hs *HTTPServer) CompleteInvite(c *models.ReqContext) response.Response {
|
||||
completeInvite := dtos.CompleteInviteForm{}
|
||||
if err := web.Bind(c.Req, &completeInvite); err != nil {
|
||||
var err error
|
||||
if err = web.Bind(c.Req, &completeInvite); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
query := models.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
|
||||
|
||||
completeInvite.Email, err = ValidateAndNormalizeEmail(completeInvite.Email)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Invalid email address provided", nil)
|
||||
}
|
||||
|
||||
completeInvite.Username = strings.TrimSpace(completeInvite.Username)
|
||||
|
||||
query := models.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
|
||||
if err := hs.tempUserService.GetTempUserByCode(c.Req.Context(), &query); err != nil {
|
||||
if errors.Is(err, models.ErrTempUserNotFound) {
|
||||
return response.Error(404, "Invite not found", nil)
|
||||
return response.Error(http.StatusNotFound, "Invite not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to get invite", err)
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get invite", err)
|
||||
}
|
||||
|
||||
invite := query.Result
|
||||
if invite.Status != models.TmpUserInvitePending {
|
||||
return response.Error(412, fmt.Sprintf("Invite cannot be used in status %s", invite.Status), nil)
|
||||
return response.Error(http.StatusPreconditionFailed, fmt.Sprintf("Invite cannot be used in status %s", invite.Status), nil)
|
||||
}
|
||||
|
||||
// In case the user is invited by email address
|
||||
if inviteMail, err := ValidateAndNormalizeEmail(invite.Email); err == nil {
|
||||
// Make sure that the email address is not amended
|
||||
if completeInvite.Email != inviteMail {
|
||||
return response.Error(http.StatusBadRequest, "The provided email is different from the address that is found in the invite", nil)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := user.CreateUserCommand{
|
||||
|
||||
@@ -28,8 +28,8 @@ func (hs *HTTPServer) SendResetPasswordEmail(c *models.ReqContext) response.Resp
|
||||
|
||||
usr, err := hs.userService.GetByLogin(c.Req.Context(), &userQuery)
|
||||
if err != nil {
|
||||
c.Logger.Info("Requested password reset for user that was not found", "user", userQuery.LoginOrEmail)
|
||||
return response.Error(http.StatusOK, "Email sent", err)
|
||||
c.Logger.Info("Requested password reset for user that was not found", "user", userQuery.LoginOrEmail, "error", err)
|
||||
return response.Error(http.StatusOK, "Email sent", nil)
|
||||
}
|
||||
|
||||
if usr.IsDisabled {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -27,15 +28,21 @@ func GetSignUpOptions(c *models.ReqContext) response.Response {
|
||||
// POST /api/user/signup
|
||||
func (hs *HTTPServer) SignUp(c *models.ReqContext) response.Response {
|
||||
form := dtos.SignUpForm{}
|
||||
if err := web.Bind(c.Req, &form); err != nil {
|
||||
var err error
|
||||
if err = web.Bind(c.Req, &form); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
if !setting.AllowUserSignUp {
|
||||
return response.Error(401, "User signup is disabled", nil)
|
||||
}
|
||||
|
||||
form.Email, err = ValidateAndNormalizeEmail(form.Email)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Invalid email address", nil)
|
||||
}
|
||||
|
||||
existing := user.GetUserByLoginQuery{LoginOrEmail: form.Email}
|
||||
_, err := hs.userService.GetByLogin(c.Req.Context(), &existing)
|
||||
_, err = hs.userService.GetByLogin(c.Req.Context(), &existing)
|
||||
if err == nil {
|
||||
return response.Error(422, "User with same email address already exists", nil)
|
||||
}
|
||||
@@ -76,6 +83,9 @@ func (hs *HTTPServer) SignUpStep2(c *models.ReqContext) response.Response {
|
||||
return response.Error(401, "User signup is disabled", nil)
|
||||
}
|
||||
|
||||
form.Email = strings.TrimSpace(form.Email)
|
||||
form.Username = strings.TrimSpace(form.Username)
|
||||
|
||||
createUserCmd := user.CreateUserCommand{
|
||||
Email: form.Email,
|
||||
Login: form.Username,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -116,9 +117,14 @@ func (hs *HTTPServer) GetUserByLoginOrEmail(c *models.ReqContext) response.Respo
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) UpdateSignedInUser(c *models.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
var err error
|
||||
if err = web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
cmd.Email = strings.TrimSpace(cmd.Email)
|
||||
cmd.Login = strings.TrimSpace(cmd.Login)
|
||||
|
||||
if setting.AuthProxyEnabled {
|
||||
if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
|
||||
return response.Error(400, "Not allowed to change email when auth proxy is using email property", nil)
|
||||
@@ -146,13 +152,18 @@ func (hs *HTTPServer) UpdateSignedInUser(c *models.ReqContext) response.Response
|
||||
func (hs *HTTPServer) UpdateUser(c *models.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
var err error
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
if err = web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
cmd.Email = strings.TrimSpace(cmd.Email)
|
||||
cmd.Login = strings.TrimSpace(cmd.Login)
|
||||
|
||||
cmd.UserID, err = strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "id is invalid", err)
|
||||
}
|
||||
|
||||
return hs.handleUpdateUser(c.Req.Context(), cmd)
|
||||
}
|
||||
|
||||
@@ -181,6 +192,16 @@ func (hs *HTTPServer) UpdateUserActiveOrg(c *models.ReqContext) response.Respons
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserCommand) response.Response {
|
||||
// external user -> user data cannot be updated
|
||||
isExternal, err := hs.isExternalUser(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to validate User", err)
|
||||
}
|
||||
|
||||
if isExternal {
|
||||
return response.Error(http.StatusForbidden, "User info cannot be updated for external Users", nil)
|
||||
}
|
||||
|
||||
if len(cmd.Login) == 0 {
|
||||
cmd.Login = cmd.Email
|
||||
if len(cmd.Login) == 0 {
|
||||
@@ -198,6 +219,20 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
|
||||
return response.Success("User updated")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) isExternalUser(ctx context.Context, userID int64) (bool, error) {
|
||||
getAuthQuery := models.GetAuthInfoQuery{UserId: userID}
|
||||
var err error
|
||||
if err = hs.authInfoService.GetAuthInfo(ctx, &getAuthQuery); err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// swagger:route GET /user/orgs signed_in_user getSignedInUserOrgList
|
||||
//
|
||||
// Organizations of the actual User.
|
||||
|
||||
@@ -13,12 +13,15 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
|
||||
authinfostore "github.com/grafana/grafana/pkg/services/login/authinfoservice/database"
|
||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers/filters"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/database"
|
||||
@@ -194,3 +197,117 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||
assert.Equal(t, 10, respJSON.Get("perPage").MustInt())
|
||||
}, mock)
|
||||
}
|
||||
|
||||
func TestHTTPServer_UpdateUser(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: settings,
|
||||
SQLStore: sqlStore,
|
||||
AccessControl: acmock.New(),
|
||||
}
|
||||
|
||||
updateUserCommand := user.UpdateUserCommand{
|
||||
Email: fmt.Sprint("admin", "@test.com"),
|
||||
Name: "admin",
|
||||
Login: "admin",
|
||||
UserID: 1,
|
||||
}
|
||||
|
||||
updateUserScenario(t, updateUserContext{
|
||||
desc: "Should return 403 when the current User is an external user",
|
||||
url: "/api/users/1",
|
||||
routePattern: "/api/users/:id",
|
||||
cmd: updateUserCommand,
|
||||
fn: func(sc *scenarioContext) {
|
||||
sc.authInfoService.ExpectedUserAuth = &models.UserAuth{}
|
||||
sc.fakeReqWithParams("PUT", sc.url, map[string]string{"id": "1"}).exec()
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
}
|
||||
|
||||
type updateUserContext struct {
|
||||
desc string
|
||||
url string
|
||||
routePattern string
|
||||
cmd user.UpdateUserCommand
|
||||
fn scenarioFunc
|
||||
}
|
||||
|
||||
func updateUserScenario(t *testing.T, ctx updateUserContext, hs *HTTPServer) {
|
||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
||||
sc := setupScenarioContext(t, ctx.url)
|
||||
|
||||
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
||||
hs.authInfoService = sc.authInfoService
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(ctx.cmd)
|
||||
c.Req.Header.Add("Content-Type", "application/json")
|
||||
sc.context = c
|
||||
sc.context.OrgID = testOrgID
|
||||
sc.context.UserID = testUserID
|
||||
|
||||
return hs.UpdateUser(c)
|
||||
})
|
||||
|
||||
sc.m.Put(ctx.routePattern, sc.defaultHandler)
|
||||
|
||||
ctx.fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTPServer_UpdateSignedInUser(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: settings,
|
||||
SQLStore: sqlStore,
|
||||
AccessControl: acmock.New(),
|
||||
}
|
||||
|
||||
updateUserCommand := user.UpdateUserCommand{
|
||||
Email: fmt.Sprint("admin", "@test.com"),
|
||||
Name: "admin",
|
||||
Login: "admin",
|
||||
UserID: 1,
|
||||
}
|
||||
|
||||
updateSignedInUserScenario(t, updateUserContext{
|
||||
desc: "Should return 403 when the current User is an external user",
|
||||
url: "/api/users/",
|
||||
routePattern: "/api/users/",
|
||||
cmd: updateUserCommand,
|
||||
fn: func(sc *scenarioContext) {
|
||||
sc.authInfoService.ExpectedUserAuth = &models.UserAuth{}
|
||||
sc.fakeReqWithParams("PUT", sc.url, map[string]string{"id": "1"}).exec()
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
}
|
||||
|
||||
func updateSignedInUserScenario(t *testing.T, ctx updateUserContext, hs *HTTPServer) {
|
||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
||||
sc := setupScenarioContext(t, ctx.url)
|
||||
|
||||
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
||||
hs.authInfoService = sc.authInfoService
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(ctx.cmd)
|
||||
c.Req.Header.Add("Content-Type", "application/json")
|
||||
sc.context = c
|
||||
sc.context.OrgID = testOrgID
|
||||
sc.context.UserID = testUserID
|
||||
|
||||
return hs.UpdateSignedInUser(c)
|
||||
})
|
||||
|
||||
sc.m.Put(ctx.routePattern, sc.defaultHandler)
|
||||
|
||||
ctx.fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
package api
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/mail"
|
||||
)
|
||||
|
||||
func jsonMap(data []byte) (map[string]string, error) {
|
||||
jsonMap := make(map[string]string)
|
||||
err := json.Unmarshal(data, &jsonMap)
|
||||
return jsonMap, err
|
||||
}
|
||||
|
||||
func ValidateAndNormalizeEmail(email string) (string, error) {
|
||||
if email == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
e, err := mail.ParseAddress(email)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return e.Address, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ import (
|
||||
func BuildFrontendPackages(c *cli.Context) error {
|
||||
version := ""
|
||||
if c.NArg() == 1 {
|
||||
version = strings.TrimPrefix(c.Args().Get(0), "v")
|
||||
// Fixes scenario where an incompatible semver is provided to lerna, which will cause the step to fail.
|
||||
// When there is an invalid semver, a frontend package won't be published anyways.
|
||||
if strings.Count(version, ".") == 2 {
|
||||
version = strings.TrimPrefix(c.Args().Get(0), "v")
|
||||
}
|
||||
}
|
||||
|
||||
cfg, mode, err := frontend.GetConfig(c, version)
|
||||
|
||||
@@ -69,7 +69,7 @@ func PublishPackages(c *cli.Context) error {
|
||||
// In test release mode, the operator should configure different GCS buckets for the package repos,
|
||||
// so should be safe.
|
||||
if cfg.ReleaseMode.Mode == config.TagMode {
|
||||
workDir, err := fsutil.CreateTempFile("")
|
||||
workDir, err := fsutil.CreateTempDir("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,3 +24,20 @@ func CreateTempFile(sfx string) (string, error) {
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
// CreateTempDir generates a temp directory, based on the provided suffix.
|
||||
// A typical generated path looks like /var/folders/abcd/abcdefg/A/1137975807/.
|
||||
func CreateTempDir(sfx string) (string, error) {
|
||||
var suffix string
|
||||
if sfx != "" {
|
||||
suffix = fmt.Sprintf("*-%s", sfx)
|
||||
} else {
|
||||
suffix = sfx
|
||||
}
|
||||
dir, err := os.MkdirTemp("", suffix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
@@ -26,3 +26,23 @@ func TestCreateTempFile(t *testing.T) {
|
||||
require.Len(t, strings.Split(pathParts[len(pathParts)-1], "-"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateTempDir(t *testing.T) {
|
||||
t.Run("empty suffix, expects pattern like: /var/folders/abcd/abcdefg/A/1137975807/", func(t *testing.T) {
|
||||
filePath, err := CreateTempFile("")
|
||||
require.NoError(t, err)
|
||||
|
||||
pathParts := strings.Split(filePath, "/")
|
||||
require.Greater(t, len(pathParts), 1)
|
||||
require.Len(t, strings.Split(pathParts[len(pathParts)-1], "-"), 1)
|
||||
})
|
||||
|
||||
t.Run("non-empty suffix, expects /var/folders/abcd/abcdefg/A/1137975807-foobar/", func(t *testing.T) {
|
||||
filePath, err := CreateTempFile("foobar")
|
||||
require.NoError(t, err)
|
||||
|
||||
pathParts := strings.Split(filePath, "/")
|
||||
require.Greater(t, len(pathParts), 1)
|
||||
require.Len(t, strings.Split(pathParts[len(pathParts)-1], "-"), 2)
|
||||
})
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func UpdateDebRepo(cfg PublishConfig, workDir string) error {
|
||||
repoName = "beta"
|
||||
}
|
||||
|
||||
repoRoot, err := fsutil.CreateTempFile("deb-repo")
|
||||
repoRoot, err := fsutil.CreateTempDir("deb-repo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func UpdateRPMRepo(cfg PublishConfig, workDir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
repoRoot, err := fsutil.CreateTempFile("rpm-repo")
|
||||
repoRoot, err := fsutil.CreateTempDir("rpm-repo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ func executeServer(configFile, homePath, pidFile, packaging string, traceDiagnos
|
||||
setting.IsEnterprise = extensions.IsEnterprise
|
||||
setting.Packaging = validPackaging(packaging)
|
||||
|
||||
metrics.SetBuildInformation(opt.Version, opt.Commit, opt.BuildBranch)
|
||||
metrics.SetBuildInformation(opt.Version, opt.Commit, opt.BuildBranch, buildstampInt64)
|
||||
|
||||
elevated, err := process.IsRunningWithElevatedPrivileges()
|
||||
if err != nil {
|
||||
|
||||
@@ -177,9 +177,6 @@ var (
|
||||
// StatsTotalDashboardVersions is a metric of total number of dashboard versions stored in Grafana.
|
||||
StatsTotalDashboardVersions prometheus.Gauge
|
||||
|
||||
// grafanaBuildVersion is a metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built
|
||||
grafanaBuildVersion *prometheus.GaugeVec
|
||||
|
||||
grafanaPluginBuildInfoDesc *prometheus.GaugeVec
|
||||
|
||||
// StatsTotalLibraryPanels is a metric of total number of library panels stored in Grafana.
|
||||
@@ -497,12 +494,6 @@ func init() {
|
||||
Namespace: ExporterName,
|
||||
}, []string{"plugin_id"})
|
||||
|
||||
grafanaBuildVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "build_info",
|
||||
Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built",
|
||||
Namespace: ExporterName,
|
||||
}, []string{"version", "revision", "branch", "goversion", "edition"})
|
||||
|
||||
grafanaPluginBuildInfoDesc = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "plugin_build_info",
|
||||
Help: "A metric with a constant '1' value labeled by pluginId, pluginType and version from which Grafana plugin was built",
|
||||
@@ -571,13 +562,28 @@ func init() {
|
||||
}
|
||||
|
||||
// SetBuildInformation sets the build information for this binary
|
||||
func SetBuildInformation(version, revision, branch string) {
|
||||
func SetBuildInformation(version, revision, branch string, buildTimestamp int64) {
|
||||
edition := "oss"
|
||||
if setting.IsEnterprise {
|
||||
edition = "enterprise"
|
||||
}
|
||||
|
||||
grafanaBuildVersion := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "build_info",
|
||||
Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built",
|
||||
Namespace: ExporterName,
|
||||
}, []string{"version", "revision", "branch", "goversion", "edition"})
|
||||
|
||||
grafanaBuildTimestamp := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "build_timestamp",
|
||||
Help: "A metric exposing when the binary was built in epoch",
|
||||
Namespace: ExporterName,
|
||||
}, []string{"version", "revision", "branch", "goversion", "edition"})
|
||||
|
||||
prometheus.MustRegister(grafanaBuildVersion, grafanaBuildTimestamp)
|
||||
|
||||
grafanaBuildVersion.WithLabelValues(version, revision, branch, runtime.Version(), edition).Set(1)
|
||||
grafanaBuildTimestamp.WithLabelValues(version, revision, branch, runtime.Version(), edition).Set(float64(buildTimestamp))
|
||||
}
|
||||
|
||||
// SetEnvironmentInformation exposes environment values provided by the operators as an `_info` metric.
|
||||
@@ -654,7 +660,6 @@ func initMetricVars() {
|
||||
StatsTotalActiveEditors,
|
||||
StatsTotalActiveAdmins,
|
||||
StatsTotalDataSources,
|
||||
grafanaBuildVersion,
|
||||
grafanaPluginBuildInfoDesc,
|
||||
StatsTotalDashboardVersions,
|
||||
StatsTotalAnnotations,
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -239,3 +240,68 @@ func TestService_RegisterFixedRoles(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermissionCacheKey(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
signedInUser *user.SignedInUser
|
||||
expected string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "should return correct key for user",
|
||||
signedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
},
|
||||
expected: "rbac-permissions-1-user-1",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "should return correct key for api key",
|
||||
signedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
ApiKeyID: 1,
|
||||
IsServiceAccount: false,
|
||||
},
|
||||
expected: "rbac-permissions-1-apikey-1",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "should return correct key for service account",
|
||||
signedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
IsServiceAccount: true,
|
||||
},
|
||||
expected: "rbac-permissions-1-service-1",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "should return correct key for matching a service account with userId -1",
|
||||
signedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
UserID: -1,
|
||||
IsServiceAccount: true,
|
||||
},
|
||||
expected: "rbac-permissions-1-service--1",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "should return error if not matching any",
|
||||
signedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
UserID: -1,
|
||||
},
|
||||
expected: "",
|
||||
expectedErr: user.ErrNoUniqueID,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
str, err := permissionCacheKey(tc.signedInUser)
|
||||
require.Equal(t, tc.expectedErr, err)
|
||||
assert.Equal(t, tc.expected, str)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,10 +262,10 @@ func (p *ResourcePermission) Contains(targetActions []string) bool {
|
||||
}
|
||||
|
||||
type SetResourcePermissionCommand struct {
|
||||
UserID int64
|
||||
TeamID int64
|
||||
BuiltinRole string
|
||||
Permission string
|
||||
UserID int64 `json:"userId,omitempty"`
|
||||
TeamID int64 `json:"teamId,omitempty"`
|
||||
BuiltinRole string `json:"builtInRole,omitempty"`
|
||||
Permission string `json:"permission"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user