diff --git a/.drone.star b/.drone.star index f025f8df415..0829f79f299 100644 --- a/.drone.star +++ b/.drone.star @@ -6,6 +6,7 @@ load('scripts/drone/pipelines/pr.star', 'pr_pipelines') load('scripts/drone/pipelines/main.star', 'main_pipelines') +load('scripts/drone/pipelines/docs.star', 'docs_pipelines') load('scripts/drone/pipelines/release.star', 'release_pipelines', 'publish_image_pipelines', 'publish_artifacts_pipelines', 'publish_npm_pipelines', 'publish_packages_pipeline') load('scripts/drone/version.star', 'version_branch_pipelines') load('scripts/drone/pipelines/cron.star', 'cronjobs') diff --git a/.drone.yml b/.drone.yml index dea2d555745..8236478e924 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,7 +11,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -23,7 +23,7 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl verify-drone @@ -43,13 +43,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition oss @@ -57,7 +57,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -68,19 +68,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -88,11 +88,14 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend trigger: event: - pull_request + paths: + exclude: + - docs/** type: docker volumes: - host: @@ -111,7 +114,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -123,7 +126,7 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} @@ -131,7 +134,7 @@ steps: depends_on: - initialize environment: {} - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition oss --build-id @@ -140,20 +143,20 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps depends_on: - initialize environment: null - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -174,7 +177,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - . scripts/build/gpg-test-vars.sh && ./bin/grabpl package --jobs 8 --edition oss @@ -184,7 +187,7 @@ steps: - build-backend - build-frontend environment: null - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ./scripts/grafana-server/start-server @@ -196,7 +199,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -273,7 +276,7 @@ steps: - build-frontend environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-storybook - commands: - yarn wait-on http://$HOST:$PORT @@ -288,26 +291,12 @@ steps: failure: always image: grafana/docker-puppeteer:1.0.0 name: test-a11y-frontend -- commands: - - ./scripts/ci-reference-docs-lint.sh ci - depends_on: - - build-frontend - image: grafana/build-container:1.4.9 - name: build-frontend-docs -- commands: - - mkdir -p /hugo/content/docs/grafana - - cp -r docs/sources/* /hugo/content/docs/grafana/latest/ - - cd /hugo && make prod - depends_on: - - build-frontend-docs - image: grafana/docs-base:latest - name: build-docs-website - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition oss -archs amd64 @@ -324,6 +313,9 @@ steps: trigger: event: - pull_request + paths: + exclude: + - docs/** type: docker volumes: - host: @@ -362,7 +354,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -379,7 +371,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -394,11 +386,14 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests trigger: event: - pull_request + paths: + exclude: + - docs/** type: docker volumes: - host: @@ -413,7 +408,7 @@ volumes: --- depends_on: [] kind: pipeline -name: main-test +name: pr-docs node: type: no-parallel platform: @@ -423,7 +418,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -435,7 +430,116 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 + name: initialize +- commands: + - yarn packages:build + - yarn packages:docsExtract + - yarn packages:docsToMarkdown + - ./scripts/ci-reference-docs-lint.sh ci + depends_on: + - initialize + image: grafana/build-container:1.5.1 + name: build-frontend-docs +- commands: + - mkdir -p /hugo/content/docs/grafana + - cp -r docs/sources/* /hugo/content/docs/grafana/latest/ + - cd /hugo && make prod + depends_on: + - build-frontend-docs + image: grafana/docs-base:latest + name: build-docs-website +trigger: + event: + - pull_request + paths: + include: + - docs/** + - packages/** +type: docker +volumes: +- host: + path: /var/run/docker.sock + name: docker +--- +depends_on: [] +kind: pipeline +name: main-docs +node: + type: no-parallel +platform: + arch: amd64 + os: linux +services: [] +steps: +- commands: + - mkdir -p bin + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl + - chmod +x bin/grabpl + image: byrnedo/alpine-curl:0.1.8 + name: grabpl +- commands: + - echo $DRONE_RUNNER_NAME + image: alpine:3.15 + name: identify-runner +- commands: + - make gen-go + - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} + - yarn install --immutable + image: grafana/build-container:1.5.1 + name: initialize +- commands: + - yarn packages:build + - yarn packages:docsExtract + - yarn packages:docsToMarkdown + - ./scripts/ci-reference-docs-lint.sh ci + depends_on: + - initialize + image: grafana/build-container:1.5.1 + name: build-frontend-docs +- commands: + - mkdir -p /hugo/content/docs/grafana + - cp -r docs/sources/* /hugo/content/docs/grafana/latest/ + - cd /hugo && make prod + depends_on: + - build-frontend-docs + image: grafana/docs-base:latest + name: build-docs-website +trigger: + branch: main + event: + - push +type: docker +volumes: +- host: + path: /var/run/docker.sock + name: docker +--- +depends_on: [] +kind: pipeline +name: main-test +node: + type: no-parallel +platform: + arch: amd64 + os: linux +services: [] +steps: +- commands: + - mkdir -p bin + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl + - chmod +x bin/grabpl + image: byrnedo/alpine-curl:0.1.8 + name: grabpl +- commands: + - echo $DRONE_RUNNER_NAME + image: alpine:3.15 + name: identify-runner +- commands: + - make gen-go + - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} + - yarn install --immutable + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl verify-drone @@ -455,13 +559,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition oss @@ -469,7 +573,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -480,19 +584,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -500,7 +604,7 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend trigger: branch: main @@ -524,7 +628,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -536,13 +640,13 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - image: grafana/drone-downstream name: trigger-enterprise-downstream settings: params: - - SOURCE_BUILD_NUMBER=${DRONE_BUILD_NUMBER} + - SOURCE_BUILD_NUMBER=${DRONE_COMMIT} - SOURCE_COMMIT=${DRONE_COMMIT} repositories: - grafana/grafana-enterprise@main @@ -555,7 +659,7 @@ steps: depends_on: - initialize environment: {} - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition oss --build-id @@ -564,7 +668,7 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin @@ -573,13 +677,13 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -600,7 +704,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - ./bin/grabpl package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise @@ -620,7 +724,7 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ./scripts/grafana-server/start-server @@ -632,7 +736,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -709,7 +813,7 @@ steps: - build-frontend environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-storybook - commands: - printenv GCP_KEY | base64 -d > /tmp/gcpkey.json @@ -749,20 +853,14 @@ steps: GRAFANA_MISC_STATS_API_KEY: from_secret: grafana_misc_stats_api_key failure: ignore - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: publish-frontend-metrics -- commands: - - ./scripts/ci-reference-docs-lint.sh ci - depends_on: - - build-frontend - image: grafana/build-container:1.4.9 - name: build-frontend-docs - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition oss @@ -834,7 +932,7 @@ steps: environment: NPM_TOKEN: from_secret: npm_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: release-canary-npm-packages - commands: - ./bin/grabpl upload-packages --edition oss --packages-bucket grafana-downloads @@ -909,7 +1007,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -926,7 +1024,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -941,7 +1039,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests trigger: branch: main @@ -977,7 +1075,7 @@ steps: name: identify-runner - commands: - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/windows/grabpl.exe + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/windows/grabpl.exe -OutFile grabpl.exe image: grafana/ci-wix:0.1.1 name: initialize @@ -1060,7 +1158,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1070,7 +1168,7 @@ steps: name: identify-runner - commands: - make gen-go - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl store-packages --edition oss --gcp-key /tmp/gcpkey.json --build-id @@ -1142,7 +1240,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1155,7 +1253,7 @@ steps: - ./bin/grabpl verify-version ${DRONE_TAG} - ./bin/grabpl gen-version ${DRONE_TAG} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl build-backend --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} @@ -1165,7 +1263,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps @@ -1174,7 +1272,7 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin @@ -1183,13 +1281,13 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -1210,7 +1308,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - ./bin/grabpl package --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise @@ -1230,14 +1328,14 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition oss --shouldSave @@ -1273,7 +1371,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -1350,7 +1448,7 @@ steps: - build-frontend environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-storybook - commands: - ./bin/grabpl upload-cdn --edition oss --src-bucket "$${PRERELEASE_BUCKET}" --src-dir @@ -1400,7 +1498,7 @@ steps: - ./scripts/build/build-npm-packages.sh ${DRONE_TAG} depends_on: - store-storybook - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-npm-packages - commands: - ./bin/grabpl artifacts npm store --tag ${DRONE_TAG} @@ -1446,7 +1544,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1459,7 +1557,7 @@ steps: - ./bin/grabpl verify-version ${DRONE_TAG} - ./bin/grabpl gen-version ${DRONE_TAG} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - |- @@ -1473,13 +1571,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition oss @@ -1487,7 +1585,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -1498,19 +1596,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -1518,7 +1616,7 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend trigger: event: @@ -1567,7 +1665,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1580,7 +1678,7 @@ steps: - ./bin/grabpl verify-version ${DRONE_TAG} - ./bin/grabpl gen-version ${DRONE_TAG} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - apt-get update @@ -1595,7 +1693,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -1610,7 +1708,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests trigger: event: @@ -1651,7 +1749,7 @@ steps: name: identify-runner - commands: - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/windows/grabpl.exe + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/windows/grabpl.exe -OutFile grabpl.exe image: grafana/ci-wix:0.1.1 name: initialize @@ -1710,7 +1808,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1725,7 +1823,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -1745,7 +1843,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl build-backend --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} @@ -1755,7 +1853,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps @@ -1764,7 +1862,7 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition enterprise --no-install-deps --sign @@ -1774,13 +1872,13 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -1801,7 +1899,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - ./bin/grabpl build-backend --jobs 8 --edition enterprise2 --github-token $${GITHUB_TOKEN} @@ -1811,7 +1909,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend-enterprise2 - commands: - ./bin/grabpl package --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} @@ -1832,14 +1930,14 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition enterprise --shouldSave @@ -1876,7 +1974,7 @@ steps: ARCH: linux-amd64 PORT: 3001 RUNDIR: scripts/grafana-server/tmp-grafana-enterprise - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -1988,7 +2086,7 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package-enterprise2 - commands: - ./bin/grabpl upload-cdn --edition enterprise2 --src-bucket "$${PRERELEASE_BUCKET}" @@ -2050,7 +2148,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2065,7 +2163,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -2085,7 +2183,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - |- @@ -2099,13 +2197,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition enterprise @@ -2113,7 +2211,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -2124,19 +2222,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition enterprise depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition enterprise depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -2144,7 +2242,7 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend - commands: - ./bin/grabpl lint-backend --edition enterprise2 @@ -2152,19 +2250,19 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend-enterprise2 - commands: - ./bin/grabpl test-backend --edition enterprise2 depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-enterprise2 - commands: - ./bin/grabpl integration-tests --edition enterprise2 depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration-enterprise2 trigger: event: @@ -2223,7 +2321,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2238,7 +2336,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -2258,7 +2356,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - apt-get update @@ -2273,7 +2371,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -2288,7 +2386,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s @@ -2297,7 +2395,7 @@ steps: - initialize environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -2306,7 +2404,7 @@ steps: - initialize environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: memcached-integration-tests trigger: event: @@ -2351,7 +2449,7 @@ steps: name: identify-runner - commands: - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/windows/grabpl.exe + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/windows/grabpl.exe -OutFile grabpl.exe - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git" - cd grafana-enterprise @@ -2426,7 +2524,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2504,7 +2602,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2565,7 +2663,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2644,7 +2742,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2706,7 +2804,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2742,7 +2840,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2785,11 +2883,11 @@ steps: - ./bin/grabpl verify-version ${DRONE_TAG} - ./bin/grabpl gen-version ${DRONE_TAG} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2811,7 +2909,7 @@ steps: environment: NPM_TOKEN: from_secret: npm_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: release-npm-packages trigger: event: @@ -2837,7 +2935,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2900,7 +2998,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2912,7 +3010,7 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} @@ -2920,7 +3018,7 @@ steps: depends_on: - initialize environment: {} - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition oss --build-id @@ -2929,7 +3027,7 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin @@ -2938,13 +3036,13 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -2965,7 +3063,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - ./bin/grabpl package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise @@ -2985,14 +3083,14 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition oss --shouldSave @@ -3028,7 +3126,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -3105,7 +3203,7 @@ steps: - build-frontend environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-storybook - commands: - ./bin/grabpl upload-cdn --edition oss --src-bucket "grafana-static-assets" @@ -3159,7 +3257,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3171,7 +3269,7 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - |- @@ -3185,13 +3283,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition oss @@ -3199,7 +3297,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -3210,19 +3308,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition oss depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -3230,7 +3328,7 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend trigger: ref: @@ -3273,7 +3371,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3285,7 +3383,7 @@ steps: - make gen-go - ./bin/grabpl gen-version --build-id ${DRONE_BUILD_NUMBER} - yarn install --immutable - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: initialize - commands: - apt-get update @@ -3300,7 +3398,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -3315,7 +3413,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests trigger: ref: @@ -3350,7 +3448,7 @@ steps: name: identify-runner - commands: - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/windows/grabpl.exe + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/windows/grabpl.exe -OutFile grabpl.exe image: grafana/ci-wix:0.1.1 name: initialize @@ -3398,7 +3496,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3413,13 +3511,13 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ - rmdir bin - mv grafana-enterprise /tmp/ - - /tmp/grabpl init-enterprise --github-token $${GITHUB_TOKEN} /tmp/grafana-enterprise + - /tmp/grabpl init-enterprise /tmp/grafana-enterprise - mv /tmp/grafana-enterprise/deployment_tools_config.json deployment_tools_config.json - mkdir bin - mv /tmp/grabpl bin/ @@ -3428,10 +3526,8 @@ steps: - yarn install --immutable depends_on: - clone-enterprise - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/build-container:1.4.9 + environment: {} + image: grafana/build-container:1.5.1 name: initialize - commands: - ./bin/grabpl build-backend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} @@ -3439,7 +3535,7 @@ steps: depends_on: - initialize environment: {} - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend - commands: - ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition enterprise --build-id @@ -3448,7 +3544,7 @@ steps: - initialize environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-frontend - commands: - ./bin/grabpl build-plugins --jobs 8 --edition enterprise --no-install-deps --sign @@ -3458,13 +3554,13 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-plugins - commands: - ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root . depends_on: - build-backend - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: validate-scuemata - commands: - '# Make sure the git tree is clean.' @@ -3485,7 +3581,7 @@ steps: - git stash pop depends_on: - validate-scuemata - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: ensure-cuetsified - commands: - ./bin/grabpl build-backend --jobs 8 --edition enterprise2 --build-id ${DRONE_BUILD_NUMBER} @@ -3493,7 +3589,7 @@ steps: depends_on: - initialize environment: {} - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-backend-enterprise2 - commands: - ./bin/grabpl package --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} @@ -3514,14 +3610,14 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: copy-packages-for-docker - commands: - ./bin/grabpl build-docker --edition enterprise --shouldSave @@ -3558,7 +3654,7 @@ steps: ARCH: linux-amd64 PORT: 3001 RUNDIR: scripts/grafana-server/tmp-grafana-enterprise - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: grafana-server - commands: - apt-get install -y netcat @@ -3635,7 +3731,7 @@ steps: - build-frontend environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: build-storybook - commands: - ./bin/grabpl upload-cdn --edition enterprise --src-bucket "grafana-static-assets" @@ -3678,7 +3774,7 @@ steps: from_secret: gpg_pub_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: package-enterprise2 - commands: - ./bin/grabpl upload-cdn --edition enterprise2 --src-bucket "grafana-static-assets" @@ -3733,7 +3829,7 @@ services: [] steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3748,13 +3844,13 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ - rmdir bin - mv grafana-enterprise /tmp/ - - /tmp/grabpl init-enterprise --github-token $${GITHUB_TOKEN} /tmp/grafana-enterprise + - /tmp/grabpl init-enterprise /tmp/grafana-enterprise - mv /tmp/grafana-enterprise/deployment_tools_config.json deployment_tools_config.json - mkdir bin - mv /tmp/grabpl bin/ @@ -3763,10 +3859,8 @@ steps: - yarn install --immutable depends_on: - clone-enterprise - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/build-container:1.4.9 + environment: {} + image: grafana/build-container:1.5.1 name: initialize - commands: - |- @@ -3780,13 +3874,13 @@ steps: - rm words_to_ignore.txt depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: codespell - commands: - ./bin/grabpl shellcheck depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: shellcheck - commands: - ./bin/grabpl lint-backend --edition enterprise @@ -3794,7 +3888,7 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend - commands: - yarn run prettier:check @@ -3805,19 +3899,19 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-frontend - commands: - ./bin/grabpl test-backend --edition enterprise depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend - commands: - ./bin/grabpl integration-tests --edition enterprise depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration - commands: - yarn run ci:test-frontend @@ -3825,7 +3919,7 @@ steps: - initialize environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-frontend - commands: - ./bin/grabpl lint-backend --edition enterprise2 @@ -3833,19 +3927,19 @@ steps: - initialize environment: CGO_ENABLED: "1" - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: lint-backend-enterprise2 - commands: - ./bin/grabpl test-backend --edition enterprise2 depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-enterprise2 - commands: - ./bin/grabpl integration-tests --edition enterprise2 depends_on: - initialize - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: test-backend-integration-enterprise2 trigger: ref: @@ -3898,7 +3992,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3913,13 +4007,13 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ - rmdir bin - mv grafana-enterprise /tmp/ - - /tmp/grabpl init-enterprise --github-token $${GITHUB_TOKEN} /tmp/grafana-enterprise + - /tmp/grabpl init-enterprise /tmp/grafana-enterprise - mv /tmp/grafana-enterprise/deployment_tools_config.json deployment_tools_config.json - mkdir bin - mv /tmp/grabpl bin/ @@ -3928,10 +4022,8 @@ steps: - yarn install --immutable depends_on: - clone-enterprise - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/build-container:1.4.9 + environment: {} + image: grafana/build-container:1.5.1 name: initialize - commands: - apt-get update @@ -3946,7 +4038,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: postgres-integration-tests - commands: - apt-get update @@ -3961,7 +4053,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: mysql-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s @@ -3970,7 +4062,7 @@ steps: - initialize environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -3979,7 +4071,7 @@ steps: - initialize environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.4.9 + image: grafana/build-container:1.5.1 name: memcached-integration-tests trigger: ref: @@ -4018,7 +4110,7 @@ steps: name: identify-runner - commands: - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.2/windows/grabpl.exe + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.5/windows/grabpl.exe -OutFile grabpl.exe - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git" - cd grafana-enterprise @@ -4215,6 +4307,6 @@ kind: secret name: gcp_upload_artifacts_key --- kind: signature -hmac: a29229ea9d7c398be08208f7294bebc889c74f2b886822c295aeabd844eabf8e +hmac: 689db4e7e75f82f24715aed3f81d93ae70d2739fe763b4fa69bb159b0b29f2e8 ... diff --git a/CHANGELOG.md b/CHANGELOG.md index f10bf7020ae..74f4e77db4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ + + +# 8.4.1 (2022-02-18) + +### Features and enhancements + +- **Cloudwatch:** Add support for AWS/PrivateLink\* metrics and dimensions. [#45515](https://github.com/grafana/grafana/pull/45515), [@szymonpk](https://github.com/szymonpk) +- **Configuration:** Add ability to customize okta login button name and icon. [#44079](https://github.com/grafana/grafana/pull/44079), [@DanCech](https://github.com/DanCech) +- **Tempo:** Switch out Select with AsyncSelect component to get loading state in Tempo Search. [#45110](https://github.com/grafana/grafana/pull/45110), [@CatPerry](https://github.com/CatPerry) + +### Bug fixes + +- **Alerting:** Fix migrations by making send_alerts_to field nullable. [#45572](https://github.com/grafana/grafana/pull/45572), [@santihernandezc](https://github.com/santihernandezc) + + # 8.4.0 (2022-02-16) diff --git a/Dockerfile b/Dockerfile index 618c0fa1e88..20aa6b5b3e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY emails emails ENV NODE_ENV production RUN yarn build -FROM golang:1.17.6-alpine3.15 as go-builder +FROM golang:1.17.7-alpine3.15 as go-builder RUN apk add --no-cache gcc g++ make diff --git a/Dockerfile.ubuntu b/Dockerfile.ubuntu index db3794d1bb1..4c3f2325a8f 100644 --- a/Dockerfile.ubuntu +++ b/Dockerfile.ubuntu @@ -21,7 +21,7 @@ COPY emails emails ENV NODE_ENV production RUN yarn build -FROM golang:1.17.6 AS go-builder +FROM golang:1.17.7 AS go-builder WORKDIR /src/grafana diff --git a/babel.config.json b/babel.config.json index 4de74eb504a..28e9f48eeed 100644 --- a/babel.config.json +++ b/babel.config.json @@ -33,6 +33,9 @@ "allowDeclareFields": true } ], + // added to mitigate https://github.com/babel/babel/issues/14289 + // package (and following line) can be removed once the issue is fixed and released + "@babel/plugin-proposal-class-properties", ["@babel/plugin-proposal-object-rest-spread", { "loose": true }], "@babel/plugin-transform-react-constant-elements", "@babel/plugin-proposal-nullish-coalescing-operator", diff --git a/conf/defaults.ini b/conf/defaults.ini index b514f480436..ad610c4d112 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -400,6 +400,9 @@ oauth_auto_login = false # OAuth state max age cookie duration in seconds. Defaults to 600 seconds. oauth_state_cookie_max_age = 600 +# Skip forced assignment of OrgID 1 or 'auto_assign_org_id' for social logins +oauth_skip_org_role_update_sync = false + # limit of api_key seconds to live before expiration api_key_max_seconds_to_live = -1 diff --git a/conf/sample.ini b/conf/sample.ini index 710a8ea4810..9f1e822933d 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -394,6 +394,9 @@ # OAuth state max age cookie duration in seconds. Defaults to 600 seconds. ;oauth_state_cookie_max_age = 600 +# Skip forced assignment of OrgID 1 or 'auto_assign_org_id' for social logins +;oauth_skip_org_role_update_sync = false + # limit of api_key seconds to live before expiration ;api_key_max_seconds_to_live = -1 diff --git a/docs/sources/administration/configuration.md b/docs/sources/administration/configuration.md index 97d454d71ad..9287a8a056c 100644 --- a/docs/sources/administration/configuration.md +++ b/docs/sources/administration/configuration.md @@ -751,6 +751,12 @@ This setting is ignored if multiple OAuth providers are configured. Default is ` How many seconds the OAuth state cookie lives before being deleted. Default is `600` (seconds) Administrators can increase this if they experience OAuth login state mismatch errors. +### oauth_skip_org_role_update_sync + +Skip forced assignment of OrgID `1` or `auto_assign_org_id` for external logins. Default is `false`. +Use this setting to distribute users with external login to multiple organizations. +Otherwise, the users' organization would get reset on every new login, for example, via AzureAD. + ### api_key_max_seconds_to_live Limit of API key seconds to live before expiration. Default is -1 (unlimited). diff --git a/docs/sources/alerting/unified-alerting/notifications/mute-timings.md b/docs/sources/alerting/unified-alerting/notifications/mute-timings.md index a41915e1660..254a9a0d26b 100644 --- a/docs/sources/alerting/unified-alerting/notifications/mute-timings.md +++ b/docs/sources/alerting/unified-alerting/notifications/mute-timings.md @@ -7,7 +7,7 @@ weight = 450 # Mute timings -A mute timing is a recurring interval of time when no new notifications for a policy are generated or sent. are sent for a policy. Use them to prevent alerts from firing a specific and reoccurring period, for example, a regular maintenance period. +A mute timing is a recurring interval of time when no new notifications for a policy are generated or sent. Use them to prevent alerts from firing a specific and reoccurring period, for example, a regular maintenance period. Similar to silences, mute timings do not prevent alert rules from being evaluated, nor do they stop alert instances from being shown in the user interface. They only prevent notifications from being created. diff --git a/docs/sources/datasources/alertmanager.md b/docs/sources/datasources/alertmanager.md index f3bcc5505c7..68422acc56c 100644 --- a/docs/sources/datasources/alertmanager.md +++ b/docs/sources/datasources/alertmanager.md @@ -26,7 +26,7 @@ apiVersion: 1 datasources: - name: Alertmanager type: alertmanager - url: http://localhost:9090 + url: http://localhost:9093 access: proxy jsonData: implementation: 'prometheus' # alternatively 'cortex' diff --git a/docs/sources/datasources/jaeger.md b/docs/sources/datasources/jaeger.md index 0f4509aa46c..f0b686cd6d8 100644 --- a/docs/sources/datasources/jaeger.md +++ b/docs/sources/datasources/jaeger.md @@ -32,6 +32,7 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t - **Data source -** Target data source. - **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`. +- **Map tag names -** When enabled, allows configuring how Jaeger tag names map to Loki label names. For example, map `service.name` to `service`. - **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Use time interval units like 5s, 1m, 3h. The default is 0. - **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0. - **Filter by Trace ID -** Toggle to append the trace ID to the Loki query. @@ -147,12 +148,15 @@ datasources: tracesToLogs: # Field with internal link pointing to a Loki data source in Grafana. # datasourceUid value must match the `datasourceUid` value of the Loki data source. - datasourceUid: loki - tags: - - cluster - - hostname - - namespace - - pod + datasourceUid: 'loki' + tags: ['job', 'instance', 'pod', 'namespace'] + mappedTags: [{ key: 'service.name', value: 'service' }] + mapTagNamesEnabled: false + spanStartTimeShift: '1h' + spanEndTimeShift: '1h' + filterByTraceID: false + filterBySpanID: false + lokiSearch: true secureJsonData: basicAuthPassword: my_password ``` diff --git a/docs/sources/datasources/tempo.md b/docs/sources/datasources/tempo.md index eeb5ad5d77e..0a688d7f2d5 100644 --- a/docs/sources/datasources/tempo.md +++ b/docs/sources/datasources/tempo.md @@ -31,6 +31,7 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t - **Data source -** Target data source. - **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`. +- **Map tag names -** When enabled, allows configuring how Tempo tag names map to Loki label names. For example, map `service.name` to `service`. - **Span start time shift -** A shift in the start time for the Loki query based on the start time for the span. To extend the time to the past, use a negative value. You can use time units, for example, 5s, 1m, 3h. The default is 0. - **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0. - **Filter by Trace ID -** Toggle to append the trace ID to the Loki query. @@ -167,6 +168,8 @@ datasources: tracesToLogs: datasourceUid: 'loki' tags: ['job', 'instance', 'pod', 'namespace'] + mappedTags: [{ key: 'service.name', value: 'service' }] + mapTagNamesEnabled: false spanStartTimeShift: '1h' spanEndTimeShift: '1h' filterByTraceID: false diff --git a/docs/sources/datasources/zipkin.md b/docs/sources/datasources/zipkin.md index 1b1f4b6df86..ed530afa4ec 100644 --- a/docs/sources/datasources/zipkin.md +++ b/docs/sources/datasources/zipkin.md @@ -32,6 +32,7 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t - **Data source -** Target data source. - **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`. +- **Map tag names -** When enabled, allows configuring how Zipkin tag names map to Loki label names. For example, map `service.name` to `service`. - **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Use time interval units like 5s, 1m, 3h. The default is 0. - **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0. - **Filter by Trace ID -** Toggle to append the trace ID to the Loki query. diff --git a/docs/sources/developers/_index.md b/docs/sources/developers/_index.md index 2d27bc4e9fb..bd4e72187a8 100644 --- a/docs/sources/developers/_index.md +++ b/docs/sources/developers/_index.md @@ -6,4 +6,10 @@ weight = 190 # Developers -This section of the documentation contains pages with resources for Grafana developers. +This section of the documentation contains pages with resources for Grafana developers: + +- [Grafana Contribution Guide - learn how you can contribute your skills to make Grafana even better.]({{< relref "contribute.md" >}}) +- [How to build a plugin]({{< relref "plugins" >}}) +- [Grafana Tutorials - step-by-step guides that help you make the most of Grafana](https://grafana.com/tutorials/) +- [Grafana Community Forums - get technical support for open source Grafana, Loki, and Tempo.](https://community.grafana.com) +- [Grafana design system - library of reusable Grafana components and guidelines that help you with contribution and plugin development](https://developers.grafana.com) diff --git a/docs/sources/enterprise/enterprise-encryption/_index.md b/docs/sources/enterprise/enterprise-encryption/_index.md index ca4a52640a3..3110e1dea52 100644 --- a/docs/sources/enterprise/enterprise-encryption/_index.md +++ b/docs/sources/enterprise/enterprise-encryption/_index.md @@ -16,10 +16,10 @@ You can choose to encrypt secrets stored in the Grafana database using a key fro Grafana integrates with the following key management systems: -- [AWS KMS]({{< relref "/using-aws-kms-to-encrypt-database-secrets.md" >}}) -- [Azure Key Vault]({{< relref "/using-azure-key-vault-to-encrypt-database-secrets.md" >}}) -- [Google Cloud KMS]({{< relref "/using-google-cloud-kms-to-encrypt-database-secrets.md" >}}) -- [Hashicorp Key Vault]({{< relref "/using-hashicorp-key-vault-to-encrypt-database-secrets.md" >}}) +- [AWS KMS]({{< relref "using-aws-kms-to-encrypt-database-secrets.md" >}}) +- [Azure Key Vault]({{< relref "using-azure-key-vault-to-encrypt-database-secrets.md" >}}) +- [Google Cloud KMS]({{< relref "using-google-cloud-kms-to-encrypt-database-secrets.md" >}}) +- [Hashicorp Key Vault]({{< relref "using-hashicorp-key-vault-to-encrypt-database-secrets.md" >}}) Refer to [Database encryption]({{< relref "../../administration/database-encryption.md" >}}) to learn more about how Grafana encrypts secrets in the database. diff --git a/docs/sources/enterprise/enterprise-encryption/using-hashicorp-key-vault-to-encrypt-database-secrets.md b/docs/sources/enterprise/enterprise-encryption/using-hashicorp-key-vault-to-encrypt-database-secrets.md index 3005656c87c..b09bd2d582b 100644 --- a/docs/sources/enterprise/enterprise-encryption/using-hashicorp-key-vault-to-encrypt-database-secrets.md +++ b/docs/sources/enterprise/enterprise-encryption/using-hashicorp-key-vault-to-encrypt-database-secrets.md @@ -5,9 +5,9 @@ keywords = ["grafana", "Hashicorp Vault integration"] weight = 3 +++ -# Using Google Cloud KMS to encrypt database secrets +# Using Hashicorp Vault to encrypt database secrets -You can use an encryption key from Google Cloud Key Management Service to encrypt secrets in the Grafana database. +You can use an encryption key from Hashicorp Vault to encrypt secrets in the Grafana database. **Prerequisites:** diff --git a/docs/sources/enterprise/query-caching.md b/docs/sources/enterprise/query-caching.md index 8014e173e6a..3b27d9b754b 100644 --- a/docs/sources/enterprise/query-caching.md +++ b/docs/sources/enterprise/query-caching.md @@ -71,6 +71,19 @@ To disable query caching for a single data source: To disable query caching for an entire Grafana instance, set the `enabled` flag to `false` in the [Query caching section of Enterprise Configuration]({{< relref "./enterprise-configuration.md#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. diff --git a/docs/sources/http_api/folder_dashboard_search.md b/docs/sources/http_api/folder_dashboard_search.md index 987665b9545..93bd9132d86 100644 --- a/docs/sources/http_api/folder_dashboard_search.md +++ b/docs/sources/http_api/folder_dashboard_search.md @@ -19,7 +19,7 @@ Query parameters: - **dashboardIds** – List of dashboard id's to search for - **folderIds** – List of folder id's to search in for dashboards - **starred** – Flag indicating if only starred Dashboards should be returned -- **limit** – Limit the number of returned results (max 5000) +- **limit** – Limit the number of returned results (max is 5000; default is 1000) - **page** – Use this parameter to access hits beyond limit. Numbering starts at 1. limit param acts as page size. Only available in Grafana v6.2+. **Example request for retrieving folders and dashboards of the general folder**: diff --git a/docs/sources/release-notes/_index.md b/docs/sources/release-notes/_index.md index 18b76d97a44..0cbb62a40e0 100644 --- a/docs/sources/release-notes/_index.md +++ b/docs/sources/release-notes/_index.md @@ -8,6 +8,7 @@ weight = 10000 Here you can find detailed release notes that list everything that is included in every release as well as notices about deprecations, breaking changes as well as changes that relate to plugin development. +- [Release notes for 8.4.1]({{< relref "release-notes-8-4-1" >}}) - [Release notes for 8.4.0-beta1]({{< relref "release-notes-8-4-0-beta1" >}}) - [Release notes for 8.3.5]({{< relref "release-notes-8-3-5" >}}) - [Release notes for 8.3.4]({{< relref "release-notes-8-3-4" >}}) diff --git a/docs/sources/release-notes/release-notes-8-4-1.md b/docs/sources/release-notes/release-notes-8-4-1.md new file mode 100644 index 00000000000..6b95dae3adf --- /dev/null +++ b/docs/sources/release-notes/release-notes-8-4-1.md @@ -0,0 +1,18 @@ ++++ +title = "Release notes for Grafana 8.4.1" +hide_menu = true ++++ + + + +# Release notes for Grafana 8.4.1 + +### Features and enhancements + +- **Cloudwatch:** Add support for AWS/PrivateLink\* metrics and dimensions. [#45515](https://github.com/grafana/grafana/pull/45515), [@szymonpk](https://github.com/szymonpk) +- **Configuration:** Add ability to customize okta login button name and icon. [#44079](https://github.com/grafana/grafana/pull/44079), [@DanCech](https://github.com/DanCech) +- **Tempo:** Switch out Select with AsyncSelect component to get loading state in Tempo Search. [#45110](https://github.com/grafana/grafana/pull/45110), [@CatPerry](https://github.com/CatPerry) + +### Bug fixes + +- **Alerting:** Fix migrations by making send_alerts_to field nullable. [#45572](https://github.com/grafana/grafana/pull/45572), [@santihernandezc](https://github.com/santihernandezc) diff --git a/package.json b/package.json index c4a21acd45f..6912959e9b3 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ }, "devDependencies": { "@babel/core": "7.16.7", + "@babel/plugin-proposal-class-properties": "7.16.7", "@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7", "@babel/plugin-proposal-object-rest-spread": "7.16.7", "@babel/plugin-proposal-optional-chaining": "7.16.7", diff --git a/packages/grafana-ui/src/components/Modal/Modal.test.tsx b/packages/grafana-ui/src/components/Modal/Modal.test.tsx index 4001fbfeee5..f629c8fb54d 100644 --- a/packages/grafana-ui/src/components/Modal/Modal.test.tsx +++ b/packages/grafana-ui/src/components/Modal/Modal.test.tsx @@ -1,27 +1,24 @@ import React from 'react'; -import { mount } from 'enzyme'; import { Modal } from './Modal'; +import { render, screen } from '@testing-library/react'; describe('Modal', () => { - it('renders without error', () => { - mount(); - }); - it('renders nothing by default or when isOpen is false', () => { - const wrapper = mount(); - expect(wrapper.html()).toBe(null); + render(); - wrapper.setProps({ ...wrapper.props(), isOpen: false }); - expect(wrapper.html()).toBe(null); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }); it('renders correct contents', () => { - const wrapper = mount( - -
Content
+ render( + +
Content
); - expect(wrapper.find('div#modal-content').length).toBe(1); - expect(wrapper.contains('Some Title')).toBeTruthy(); + + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(screen.getByLabelText('Some Title')).toBeInTheDocument(); + + expect(screen.getByTestId('modal-content')).toBeInTheDocument(); }); }); diff --git a/packages/grafana-ui/src/components/Modal/Modal.tsx b/packages/grafana-ui/src/components/Modal/Modal.tsx index f053d665b59..a159a2825ec 100644 --- a/packages/grafana-ui/src/components/Modal/Modal.tsx +++ b/packages/grafana-ui/src/components/Modal/Modal.tsx @@ -1,7 +1,9 @@ import { cx } from '@emotion/css'; import { FocusScope } from '@react-aria/focus'; -import { OverlayContainer } from '@react-aria/overlays'; -import React, { PropsWithChildren, useCallback, useEffect } from 'react'; +import { useDialog } from '@react-aria/dialog'; + +import { OverlayContainer, useOverlay } from '@react-aria/overlays'; +import React, { PropsWithChildren, useRef } from 'react'; import { useTheme2 } from '../../themes'; import { IconName } from '../../types'; @@ -39,33 +41,24 @@ export function Modal(props: PropsWithChildren) { closeOnBackdropClick = true, className, contentClassName, - onDismiss: propsOnDismiss, + onDismiss, onClickBackdrop, trapFocus = true, } = props; const theme = useTheme2(); const styles = getModalStyles(theme); - const onDismiss = useCallback(() => { - if (propsOnDismiss) { - propsOnDismiss(); - } - }, [propsOnDismiss]); - useEffect(() => { - const onEscKey = (ev: KeyboardEvent) => { - if (ev.key === 'Esc' || ev.key === 'Escape') { - onDismiss(); - } - }; - if (isOpen && closeOnEscape) { - document.addEventListener('keydown', onEscKey, false); - } else { - document.removeEventListener('keydown', onEscKey, false); - } - return () => { - document.removeEventListener('keydown', onEscKey, false); - }; - }, [closeOnEscape, isOpen, onDismiss]); + const ref = useRef(null); + + // Handle interacting outside the dialog and pressing + // the Escape key to close the modal. + const { overlayProps, underlayProps } = useOverlay( + { isKeyboardDismissDisabled: closeOnEscape, isOpen, onClose: onDismiss }, + ref + ); + + // Get props for the dialog and its title + const { dialogProps, titleProps } = useDialog({}, ref); if (!isOpen) { return null; @@ -78,16 +71,17 @@ export function Modal(props: PropsWithChildren) {
- {/* - tabIndex=-1 is needed here to support highlighting text within the modal when using FocusScope - see https://github.com/adobe/react-spectrum/issues/1604#issuecomment-781574668 - */} -
+
- {typeof title === 'string' && } - {typeof title !== 'string' && title} + {typeof title === 'string' && } + { + // FIXME: custom title components won't get an accessible title. + // Do we really want to support them or shall we just limit this ModalTabsHeader? + typeof title !== 'string' && title + }
@@ -130,11 +124,12 @@ function ModalButtonRow({ leftItems, children }: { leftItems?: React.ReactNode; Modal.ButtonRow = ModalButtonRow; interface DefaultModalHeaderProps { + id?: string; title: string; icon?: IconName; iconTooltip?: string; } -function DefaultModalHeader({ icon, iconTooltip, title }: DefaultModalHeaderProps): JSX.Element { - return ; +function DefaultModalHeader({ icon, iconTooltip, title, id }: DefaultModalHeaderProps): JSX.Element { + return ; } diff --git a/packages/grafana-ui/src/components/Modal/ModalHeader.tsx b/packages/grafana-ui/src/components/Modal/ModalHeader.tsx index 4873b394c73..6d5c2c1acaf 100644 --- a/packages/grafana-ui/src/components/Modal/ModalHeader.tsx +++ b/packages/grafana-ui/src/components/Modal/ModalHeader.tsx @@ -5,6 +5,7 @@ import { useStyles2 } from '../../themes'; interface Props { title: string; + id?: string; /** @deprecated */ icon?: IconName; /** @deprecated */ @@ -12,12 +13,14 @@ interface Props { } /** @internal */ -export const ModalHeader: React.FC = ({ icon, iconTooltip, title, children }) => { +export const ModalHeader: React.FC = ({ icon, iconTooltip, title, children, id }) => { const styles = useStyles2(getModalStyles); return ( <> -

{title}

+

+ {title} +

{children} ); diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 8060092a4a6..92d19b4afdf 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -11,6 +11,8 @@ import ( "net/http" "net/url" + "golang.org/x/oauth2" + "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" @@ -21,7 +23,6 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/web" - "golang.org/x/oauth2" ) var ( @@ -132,7 +133,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) response.Response { return nil } - hashedState := hashStatecode(state, provider.ClientSecret) + hashedState := hs.hashStatecode(state, provider.ClientSecret) cookies.WriteCookie(ctx.Resp, OauthStateCookieName, hashedState, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg) if provider.HostedDomain != "" { opts = append(opts, oauth2.SetAuthURLParam("hd", provider.HostedDomain)) @@ -155,7 +156,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) response.Response { return nil } - queryState := hashStatecode(ctx.Query("state"), provider.ClientSecret) + queryState := hs.hashStatecode(ctx.Query("state"), provider.ClientSecret) oauthLogger.Info("state check", "queryState", queryState, "cookieState", cookieState) if cookieState != queryState { hs.handleOAuthLoginError(ctx, loginInfo, LoginError{ @@ -234,7 +235,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) response.Response { return nil } - loginInfo.ExternalUser = *buildExternalUserInfo(token, userInfo, name) + loginInfo.ExternalUser = *hs.buildExternalUserInfo(token, userInfo, name) loginInfo.User, err = syncUser(ctx, &loginInfo.ExternalUser, connect) if err != nil { hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err) @@ -265,7 +266,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) response.Response { } // buildExternalUserInfo returns a ExternalUserInfo struct from OAuth user profile -func buildExternalUserInfo(token *oauth2.Token, userInfo *social.BasicUserInfo, name string) *models.ExternalUserInfo { +func (hs *HTTPServer) buildExternalUserInfo(token *oauth2.Token, userInfo *social.BasicUserInfo, name string) *models.ExternalUserInfo { oauthLogger.Debug("Building external user info from OAuth user info") extUser := &models.ExternalUserInfo{ @@ -279,13 +280,13 @@ func buildExternalUserInfo(token *oauth2.Token, userInfo *social.BasicUserInfo, Groups: userInfo.Groups, } - if userInfo.Role != "" { + if userInfo.Role != "" && !hs.Cfg.OAuthSkipOrgRoleUpdateSync { rt := models.RoleType(userInfo.Role) if rt.IsValid() { // The user will be assigned a role in either the auto-assigned organization or in the default one var orgID int64 - if setting.AutoAssignOrg && setting.AutoAssignOrgId > 0 { - orgID = int64(setting.AutoAssignOrgId) + if hs.Cfg.AutoAssignOrg && hs.Cfg.AutoAssignOrgId > 0 { + orgID = int64(hs.Cfg.AutoAssignOrgId) plog.Debug("The user has a role assignment and organization membership is auto-assigned", "role", userInfo.Role, "orgId", orgID) } else { @@ -327,8 +328,8 @@ func syncUser( return cmd.Result, nil } -func hashStatecode(code, seed string) string { - hashBytes := sha256.Sum256([]byte(code + setting.SecretKey + seed)) +func (hs *HTTPServer) hashStatecode(code, seed string) string { + hashBytes := sha256.Sum256([]byte(code + hs.Cfg.SecretKey + seed)) return hex.EncodeToString(hashBytes[:]) } diff --git a/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_deks.go b/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_deks.go index 0d97599c491..780e9d306e5 100644 --- a/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_deks.go +++ b/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_deks.go @@ -3,7 +3,6 @@ package secretsmigrations import ( "context" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/cmd/grafana-cli/runner" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" "github.com/grafana/grafana/pkg/services/featuremgmt" diff --git a/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_secrets.go b/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_secrets.go index 067a5d10d36..257259ad2a7 100644 --- a/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_secrets.go +++ b/pkg/cmd/grafana-cli/commands/secretsmigrations/reencrypt_secrets.go @@ -4,9 +4,9 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/cmd/grafana-cli/runner" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -17,69 +17,123 @@ import ( "xorm.io/xorm" ) -func (s simpleSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) error { +func (s simpleSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) { var rows []struct { Id int - Secret string + Secret []byte } if err := sess.Table(s.tableName).Select(fmt.Sprintf("id, %s as secret", s.columnName)).Find(&rows); err != nil { - return err + logger.Warn("Could not find any secret to re-encrypt", "table", s.tableName) + return } + var anyFailure bool + for _, row := range rows { if len(row.Secret) == 0 { continue } - var ( - err error - decoded = []byte(row.Secret) - ) - - if s.isBase64Encoded { - decoded, err = base64.StdEncoding.DecodeString(row.Secret) - if err != nil { - return err - } - } - - decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) + decrypted, err := secretsSrv.Decrypt(context.Background(), row.Secret) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue } encrypted, err := secretsSrv.EncryptWithDBSession(context.Background(), decrypted, secrets.WithoutScope(), sess) if err != nil { - return err + anyFailure = true + logger.Warn("Could not encrypt secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue } - encoded := string(encrypted) - if s.isBase64Encoded { - encoded = base64.StdEncoding.EncodeToString(encrypted) - } - - updateSQL := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", s.tableName, s.columnName) - if _, err := sess.Exec(updateSQL, encoded, row.Id); err != nil { - return err + updateSQL := fmt.Sprintf("UPDATE %s SET %s = ?, updated = ? WHERE id = ?", s.tableName, s.columnName) + if _, err = sess.Exec(updateSQL, encrypted, nowInUTC(), row.Id); err != nil { + anyFailure = true + logger.Warn("Could not update secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue } } - logger.Infof("Column %s from %s has been re-encrypted successfully\n", s.columnName, s.tableName) - - return nil + if anyFailure { + logger.Warn(fmt.Sprintf("Column %s from %s has been re-encrypted with errors", s.columnName, s.tableName)) + } else { + logger.Info(fmt.Sprintf("Column %s from %s has been re-encrypted successfully", s.columnName, s.tableName)) + } } -func (s jsonSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) error { +func (s b64Secret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) { + var rows []struct { + Id int + Secret string + } + + if err := sess.Table(s.tableName).Select(fmt.Sprintf("id, %s as secret", s.columnName)).Find(&rows); err != nil { + logger.Warn("Could not find any secret to re-encrypt", "table", s.tableName) + return + } + + var anyFailure bool + + for _, row := range rows { + if len(row.Secret) == 0 { + continue + } + + decoded, err := base64.StdEncoding.DecodeString(row.Secret) + if err != nil { + anyFailure = true + logger.Warn("Could not decode base64-encoded secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) + if err != nil { + anyFailure = true + logger.Warn("Could not decrypt secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + encrypted, err := secretsSrv.EncryptWithDBSession(context.Background(), decrypted, secrets.WithoutScope(), sess) + if err != nil { + anyFailure = true + logger.Warn("Could not encrypt secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + encoded := base64.StdEncoding.EncodeToString(encrypted) + updateSQL := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", s.tableName, s.columnName) + _, err = sess.Exec(updateSQL, encoded, row.Id) + + if err != nil { + anyFailure = true + logger.Warn("Could not update secret while re-encrypting it", "table", s.tableName, "id", row.Id, "error", err) + continue + } + } + + if anyFailure { + logger.Warn(fmt.Sprintf("Column %s from %s has been re-encrypted with errors", s.columnName, s.tableName)) + } else { + logger.Info(fmt.Sprintf("Column %s from %s has been re-encrypted successfully", s.columnName, s.tableName)) + } +} + +func (s jsonSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) { var rows []struct { Id int SecureJsonData map[string][]byte } if err := sess.Table(s.tableName).Cols("id", "secure_json_data").Find(&rows); err != nil { - return err + logger.Warn("Could not find any secret to re-encrypt", "table", s.tableName) + return } + var anyFailure bool + for _, row := range rows { if len(row.SecureJsonData) == 0 { continue @@ -87,29 +141,38 @@ func (s jsonSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Ses decrypted, err := secretsSrv.DecryptJsonData(context.Background(), row.SecureJsonData) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt secrets while re-encrypting them", "table", s.tableName, "id", row.Id, "error", err) + continue } - var toUpdate struct { + toUpdate := struct { SecureJsonData map[string][]byte - } + Updated string + }{Updated: nowInUTC()} toUpdate.SecureJsonData, err = secretsSrv.EncryptJsonDataWithDBSession(context.Background(), decrypted, secrets.WithoutScope(), sess) if err != nil { - return err + anyFailure = true + logger.Warn("Could not re-encrypt secrets", "table", s.tableName, "id", row.Id, "error", err) + continue } if _, err := sess.Table(s.tableName).Where("id = ?", row.Id).Update(toUpdate); err != nil { - return err + anyFailure = true + logger.Warn("Could not update secrets while re-encrypting them", "table", s.tableName, "id", row.Id, "error", err) + continue } } - logger.Infof("Secure json data from %s has been re-encrypted successfully\n", s.tableName) - - return nil + if anyFailure { + logger.Warn(fmt.Sprintf("Secure json data secrets from %s have been re-encrypted with errors", s.tableName)) + } else { + logger.Info(fmt.Sprintf("Secure json data secrets from %s have been re-encrypted successfully", s.tableName)) + } } -func (s alertingSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) error { +func (s alertingSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm.Session) { var results []struct { Id int AlertmanagerConfiguration string @@ -117,14 +180,19 @@ func (s alertingSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm selectSQL := "SELECT id, alertmanager_configuration FROM alert_configuration" if err := sess.SQL(selectSQL).Find(&results); err != nil { - return err + logger.Warn("Could not find any alert_configuration secret to re-encrypt") + return } + var anyFailure bool + for _, result := range results { result := result postableUserConfig, err := notifier.Load([]byte(result.AlertmanagerConfiguration)) if err != nil { - return err + anyFailure = true + logger.Warn("Could not load alert_configuration while re-encrypting it", "id", result.Id, "error", err) + continue } for _, receiver := range postableUserConfig.AlertmanagerConfig.Receivers { @@ -132,17 +200,23 @@ func (s alertingSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm for k, v := range gmr.SecureSettings { decoded, err := base64.StdEncoding.DecodeString(v) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decode base64-encoded alert_configuration secret", "id", result.Id, "key", k, "error", err) + continue } decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt alert_configuration secret", "id", result.Id, "key", k, "error", err) + continue } reencrypted, err := secretsSrv.EncryptWithDBSession(context.Background(), decrypted, secrets.WithoutScope(), sess) if err != nil { - return err + anyFailure = true + logger.Warn("Could not re-encrypt alert_configuration secret", "id", result.Id, "key", k, "error", err) + continue } gmr.SecureSettings[k] = base64.StdEncoding.EncodeToString(reencrypted) @@ -152,18 +226,24 @@ func (s alertingSecret) reencrypt(secretsSrv *manager.SecretsService, sess *xorm marshalled, err := json.Marshal(postableUserConfig) if err != nil { - return err + anyFailure = true + logger.Warn("Could not marshal alert_configuration while re-encrypting it", "id", result.Id, "error", err) + continue } result.AlertmanagerConfiguration = string(marshalled) if _, err := sess.Table("alert_configuration").Where("id = ?", result.Id).Update(&result); err != nil { - return err + anyFailure = true + logger.Warn("Could not update alert_configuration secret while re-encrypting it", "id", result.Id, "error", err) + continue } } - logger.Info("Alerting secrets has been re-encrypted successfully\n") - - return nil + if anyFailure { + logger.Warn("Alerting configuration secrets have been re-encrypted with errors") + } else { + logger.Info("Alerting configuration secrets have been re-encrypted successfully") + } } func ReEncryptSecrets(_ utils.CommandLine, runner runner.Runner) error { @@ -173,22 +253,27 @@ func ReEncryptSecrets(_ utils.CommandLine, runner runner.Runner) error { } toMigrate := []interface { - reencrypt(*manager.SecretsService, *xorm.Session) error + reencrypt(*manager.SecretsService, *xorm.Session) }{ - simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted", isBase64Encoded: false}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token", isBase64Encoded: true}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token", isBase64Encoded: true}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type", isBase64Encoded: true}, + simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted"}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token"}}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token"}}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type"}}, jsonSecret{tableName: "data_source"}, jsonSecret{tableName: "plugin_setting"}, alertingSecret{}, } - return runner.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { - for _, m := range toMigrate { - if err := m.reencrypt(runner.SecretsService, sess.Session); err != nil { - return err + return runner.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.New(fmt.Sprint(r)) + logger.Error("Secrets re-encryption failed, rolling back transaction...", "error", err) } + }() + + for _, m := range toMigrate { + m.reencrypt(runner.SecretsService, sess.Session) } return nil diff --git a/pkg/cmd/grafana-cli/commands/secretsmigrations/rollback_secrets.go b/pkg/cmd/grafana-cli/commands/secretsmigrations/rollback_secrets.go index 619cbfb8f6e..fc17c10aaed 100644 --- a/pkg/cmd/grafana-cli/commands/secretsmigrations/rollback_secrets.go +++ b/pkg/cmd/grafana-cli/commands/secretsmigrations/rollback_secrets.go @@ -4,14 +4,13 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" - "github.com/grafana/grafana/pkg/services/featuremgmt" - - "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/cmd/grafana-cli/runner" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/ngalert/notifier" "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -23,14 +22,15 @@ func (s simpleSecret) rollback( encryptionSrv encryption.Internal, sess *xorm.Session, secretKey string, -) error { +) (anyFailure bool) { var rows []struct { Id int - Secret string + Secret []byte } if err := sess.Table(s.tableName).Select(fmt.Sprintf("id, %s as secret", s.columnName)).Find(&rows); err != nil { - return err + logger.Warn("Could not find any secret to roll back", "table", s.tableName) + return true } for _, row := range rows { @@ -38,42 +38,95 @@ func (s simpleSecret) rollback( continue } - var ( - err error - decoded = []byte(row.Secret) - ) - - if s.isBase64Encoded { - decoded, err = base64.StdEncoding.DecodeString(row.Secret) - if err != nil { - return err - } - } - - decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) + decrypted, err := secretsSrv.Decrypt(context.Background(), row.Secret) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue } encrypted, err := encryptionSrv.Encrypt(context.Background(), decrypted, secretKey) if err != nil { - return err + anyFailure = true + logger.Warn("Could not encrypt secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue } - encoded := string(encrypted) - if s.isBase64Encoded { - encoded = base64.StdEncoding.EncodeToString(encrypted) - } - - updateSQL := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", s.tableName, s.columnName) - if _, err := sess.Exec(updateSQL, encoded, row.Id); err != nil { - return err + updateSQL := fmt.Sprintf("UPDATE %s SET %s = ?, updated = ? WHERE id = ?", s.tableName, s.columnName) + if _, err = sess.Exec(updateSQL, encrypted, nowInUTC(), row.Id); err != nil { + anyFailure = true + logger.Warn("Could not update secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue } } - logger.Infof("Column %s from %s have been rolled back successfully\n", s.columnName, s.tableName) + if anyFailure { + logger.Warn(fmt.Sprintf("Column %s from %s has been rolled back with errors", s.columnName, s.tableName)) + } else { + logger.Info(fmt.Sprintf("Column %s from %s has been rolled back successfully", s.columnName, s.tableName)) + } - return nil + return anyFailure +} + +func (s b64Secret) rollback( + secretsSrv *manager.SecretsService, + encryptionSrv encryption.Internal, + sess *xorm.Session, + secretKey string, +) (anyFailure bool) { + var rows []struct { + Id int + Secret string + } + + if err := sess.Table(s.tableName).Select(fmt.Sprintf("id, %s as secret", s.columnName)).Find(&rows); err != nil { + logger.Warn("Could not find any secret to roll back", "table", s.tableName) + return true + } + + for _, row := range rows { + if len(row.Secret) == 0 { + continue + } + + decoded, err := base64.StdEncoding.DecodeString(row.Secret) + if err != nil { + anyFailure = true + logger.Warn("Could not decode base64-encoded secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) + if err != nil { + anyFailure = true + logger.Warn("Could not decrypt secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + encrypted, err := encryptionSrv.Encrypt(context.Background(), decrypted, secretKey) + if err != nil { + anyFailure = true + logger.Warn("Could not encrypt secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue + } + + encoded := base64.StdEncoding.EncodeToString(encrypted) + updateSQL := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", s.tableName, s.columnName) + if _, err := sess.Exec(updateSQL, encoded, row.Id); err != nil { + anyFailure = true + logger.Warn("Could not update secret while rolling it back", "table", s.tableName, "id", row.Id, "error", err) + continue + } + } + + if anyFailure { + logger.Warn(fmt.Sprintf("Column %s from %s has been rolled back with errors", s.columnName, s.tableName)) + } else { + logger.Info(fmt.Sprintf("Column %s from %s has been rolled back successfully", s.columnName, s.tableName)) + } + + return anyFailure } func (s jsonSecret) rollback( @@ -81,14 +134,15 @@ func (s jsonSecret) rollback( encryptionSrv encryption.Internal, sess *xorm.Session, secretKey string, -) error { +) (anyFailure bool) { var rows []struct { Id int SecureJsonData map[string][]byte } if err := sess.Table(s.tableName).Cols("id", "secure_json_data").Find(&rows); err != nil { - return err + logger.Warn("Could not find any secret to roll back", "table", s.tableName) + return true } for _, row := range rows { @@ -98,26 +152,35 @@ func (s jsonSecret) rollback( decrypted, err := secretsSrv.DecryptJsonData(context.Background(), row.SecureJsonData) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt secrets while rolling them back", "table", s.tableName, "id", row.Id, "error", err) + continue } - var toUpdate struct { + toUpdate := struct { SecureJsonData map[string][]byte - } + Updated string + }{Updated: nowInUTC()} toUpdate.SecureJsonData, err = encryptionSrv.EncryptJsonData(context.Background(), decrypted, secretKey) if err != nil { - return err + logger.Warn("Could not re-encrypt secrets while rolling them back", "table", s.tableName, "id", row.Id, "error", err) + continue } if _, err := sess.Table(s.tableName).Where("id = ?", row.Id).Update(toUpdate); err != nil { - return err + logger.Warn("Could not update secrets while rolling them back", "table", s.tableName, "id", row.Id, "error", err) + continue } } - logger.Infof("Secure json data from %s have been rolled back successfully\n", s.tableName) + if anyFailure { + logger.Warn(fmt.Sprintf("Secure json data secrets from %s have been rolled back with errors", s.tableName)) + } else { + logger.Info(fmt.Sprintf("Secure json data secrets from %s have been rolled back successfully", s.tableName)) + } - return nil + return anyFailure } func (s alertingSecret) rollback( @@ -125,7 +188,7 @@ func (s alertingSecret) rollback( encryptionSrv encryption.Internal, sess *xorm.Session, secretKey string, -) error { +) (anyFailure bool) { var results []struct { Id int AlertmanagerConfiguration string @@ -133,14 +196,17 @@ func (s alertingSecret) rollback( selectSQL := "SELECT id, alertmanager_configuration FROM alert_configuration" if err := sess.SQL(selectSQL).Find(&results); err != nil { - return err + logger.Warn("Could not find any alert_configuration secret to roll back") + return true } for _, result := range results { result := result postableUserConfig, err := notifier.Load([]byte(result.AlertmanagerConfiguration)) if err != nil { - return err + anyFailure = true + logger.Warn("Could not load configuration (alert_configuration with id: %d) while rolling it back", result.Id, err) + continue } for _, receiver := range postableUserConfig.AlertmanagerConfig.Receivers { @@ -148,17 +214,23 @@ func (s alertingSecret) rollback( for k, v := range gmr.SecureSettings { decoded, err := base64.StdEncoding.DecodeString(v) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decode base64-encoded secret (alert_configuration with id: %d, key)", k, result.Id, err) + continue } decrypted, err := secretsSrv.Decrypt(context.Background(), decoded) if err != nil { - return err + anyFailure = true + logger.Warn("Could not decrypt secret (alert_configuration with id: %d, key)", k, result.Id, err) + continue } reencrypted, err := encryptionSrv.Encrypt(context.Background(), decrypted, secretKey) if err != nil { - return err + anyFailure = true + logger.Warn("Could not re-encrypt secret (alert_configuration with id: %d, key)", k, result.Id, err) + continue } gmr.SecureSettings[k] = base64.StdEncoding.EncodeToString(reencrypted) @@ -168,18 +240,26 @@ func (s alertingSecret) rollback( marshalled, err := json.Marshal(postableUserConfig) if err != nil { - return err + anyFailure = true + logger.Warn("Could not marshal configuration (alert_configuration with id: %d) while rolling it back", result.Id, err) + continue } result.AlertmanagerConfiguration = string(marshalled) if _, err := sess.Table("alert_configuration").Where("id = ?", result.Id).Update(&result); err != nil { - return err + anyFailure = true + logger.Warn("Could not update secret (alert_configuration with id: %d) while rolling it back", result.Id, err) + continue } } - logger.Info("Alerting secrets have rolled re-encrypted successfully\n") + if anyFailure { + logger.Warn("Alerting configuration secrets have been rolled back with errors") + } else { + logger.Info("Alerting configuration secrets have been rolled back successfully") + } - return nil + return anyFailure } func RollBackSecrets(_ utils.CommandLine, runner runner.Runner) error { @@ -188,31 +268,41 @@ func RollBackSecrets(_ utils.CommandLine, runner runner.Runner) error { return nil } - toMigrate := []interface { - rollback(*manager.SecretsService, encryption.Internal, *xorm.Session, string) error + toRollback := []interface { + rollback(*manager.SecretsService, encryption.Internal, *xorm.Session, string) bool }{ - simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted", isBase64Encoded: false}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token", isBase64Encoded: true}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token", isBase64Encoded: true}, - simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type", isBase64Encoded: true}, + simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted"}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token"}}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token"}}, + b64Secret{simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type"}}, jsonSecret{tableName: "data_source"}, jsonSecret{tableName: "plugin_setting"}, alertingSecret{}, } - return runner.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { - for _, m := range toMigrate { - if err := m.rollback( - runner.SecretsService, - runner.EncryptionService, - sess.Session, - runner.Cfg.SecretKey); err != nil { - return err + return runner.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.New(fmt.Sprint(r)) + logger.Error("Secrets roll back failed, rolling back transaction...", "error", err) + } + }() + + var anyFailure bool + + for _, r := range toRollback { + if failed := r.rollback(runner.SecretsService, runner.EncryptionService, sess.Session, runner.Cfg.SecretKey); failed { + anyFailure = true } } - if _, err := sess.Exec("DELETE FROM data_keys"); err != nil { - logger.Warn("Error while cleaning up data keys table...", "err", err) + if anyFailure { + logger.Warn("Some errors happened, not cleaning up data keys table...") + return nil + } + + if _, sqlErr := sess.Exec("DELETE FROM data_keys"); sqlErr != nil { + logger.Warn("Error while cleaning up data keys table...", "error", sqlErr) } return nil diff --git a/pkg/cmd/grafana-cli/commands/secretsmigrations/secretsmigrations.go b/pkg/cmd/grafana-cli/commands/secretsmigrations/secretsmigrations.go new file mode 100644 index 00000000000..a75e2e9b329 --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/secretsmigrations/secretsmigrations.go @@ -0,0 +1,28 @@ +package secretsmigrations + +import ( + "time" + + "github.com/grafana/grafana/pkg/infra/log" +) + +type simpleSecret struct { + tableName string + columnName string +} + +type b64Secret struct { + simpleSecret +} + +type jsonSecret struct { + tableName string +} + +type alertingSecret struct{} + +func nowInUTC() string { + return time.Now().UTC().Format("2006-01-02 15:04:05") +} + +var logger = log.New("secrets.migrations") diff --git a/pkg/cmd/grafana-cli/commands/secretsmigrations/types.go b/pkg/cmd/grafana-cli/commands/secretsmigrations/types.go deleted file mode 100644 index b272983f26d..00000000000 --- a/pkg/cmd/grafana-cli/commands/secretsmigrations/types.go +++ /dev/null @@ -1,13 +0,0 @@ -package secretsmigrations - -type simpleSecret struct { - tableName string - columnName string - isBase64Encoded bool -} - -type jsonSecret struct { - tableName string -} - -type alertingSecret struct{} diff --git a/pkg/services/kmsproviders/kmsproviders.go b/pkg/services/kmsproviders/kmsproviders.go index 6de895f5847..c5baf11a344 100644 --- a/pkg/services/kmsproviders/kmsproviders.go +++ b/pkg/services/kmsproviders/kmsproviders.go @@ -17,3 +17,11 @@ const ( type Service interface { Provide() (map[secrets.ProviderID]secrets.Provider, error) } + +func NormalizeProviderID(id secrets.ProviderID) secrets.ProviderID { + if id == Legacy { + return Default + } + + return id +} diff --git a/pkg/services/secrets/database/database.go b/pkg/services/secrets/database/database.go index 19a5e509e93..c4c0aeb4016 100644 --- a/pkg/services/secrets/database/database.go +++ b/pkg/services/secrets/database/database.go @@ -6,6 +6,7 @@ import ( "time" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/kmsproviders" "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" "xorm.io/xorm" @@ -100,26 +101,50 @@ func (ss *SecretsStoreImpl) ReEncryptDataKeys( } for _, k := range keys { - provider, ok := providers[k.Provider] + provider, ok := providers[kmsproviders.NormalizeProviderID(k.Provider)] if !ok { - return fmt.Errorf("could not find encryption provider '%s'", k.Provider) + ss.log.Warn( + "Could not find provider to re-encrypt data encryption key", + "key_id", k.Name, + "provider", k.Provider, + ) + continue } decrypted, err := provider.Decrypt(ctx, k.EncryptedData) if err != nil { - return err + ss.log.Warn( + "Error while decrypting data encryption key to re-encrypt it", + "key_id", k.Name, + "provider", k.Provider, + "err", err, + ) + continue } // Updating current data key by re-encrypting it with current provider. // Accessing the current provider within providers map should be safe. k.Provider = currProvider + k.Updated = time.Now() k.EncryptedData, err = providers[currProvider].Encrypt(ctx, decrypted) if err != nil { - return err + ss.log.Warn( + "Error while re-encrypting data encryption key", + "key_id", k.Name, + "provider", k.Provider, + "err", err, + ) + continue } if _, err := sess.Table(dataKeysTable).Where("name = ?", k.Name).Update(k); err != nil { - return err + ss.log.Warn( + "Error while re-encrypting data encryption key", + "key_id", k.Name, + "provider", k.Provider, + "err", err, + ) + continue } } diff --git a/pkg/services/secrets/manager/manager.go b/pkg/services/secrets/manager/manager.go index b31f0c7f73b..f10e3da9eb2 100644 --- a/pkg/services/secrets/manager/manager.go +++ b/pkg/services/secrets/manager/manager.go @@ -48,7 +48,7 @@ func ProvideSecretsService( logger := log.New("secrets") enabled := features.IsEnabled(featuremgmt.FlagEnvelopeEncryption) - currentProviderID := normalizeProviderID(secrets.ProviderID( + currentProviderID := kmsproviders.NormalizeProviderID(secrets.ProviderID( settings.KeyValue("security", "encryption_provider").MustString(kmsproviders.Default), )) @@ -79,14 +79,6 @@ func ProvideSecretsService( return s, nil } -func normalizeProviderID(id secrets.ProviderID) secrets.ProviderID { - if id == kmsproviders.Legacy { - return kmsproviders.Default - } - - return id -} - func (s *SecretsService) registerUsageMetrics() { s.usageStats.RegisterMetricsFunc(func(context.Context) (map[string]interface{}, error) { usageMetrics := make(map[string]interface{}) @@ -330,7 +322,7 @@ func (s *SecretsService) dataKey(ctx context.Context, name string) ([]byte, erro } // 2. decrypt data key - provider, exists := s.providers[normalizeProviderID(dataKey.Provider)] + provider, exists := s.providers[kmsproviders.NormalizeProviderID(dataKey.Provider)] if !exists { return nil, fmt.Errorf("could not find encryption provider '%s'", dataKey.Provider) } diff --git a/pkg/services/sqlstore/migrations/migrations_test.go b/pkg/services/sqlstore/migrations/migrations_test.go index 83a26cc52df..96218fb3c93 100644 --- a/pkg/services/sqlstore/migrations/migrations_test.go +++ b/pkg/services/sqlstore/migrations/migrations_test.go @@ -155,6 +155,11 @@ func TestMigrationLock(t *testing.T) { func TestMigratorLocking(t *testing.T) { dbType := getDBType() testDB := getTestDB(t, dbType) + // skip for SQLite for now since it occasionally fails for not clear reason + // anyway starting migrations concurretly for the same migrator is impossible use case + if dbType == SQLite { + t.Skip() + } x, err := xorm.NewEngine(testDB.DriverName, testDB.ConnStr) require.NoError(t, err) @@ -181,7 +186,7 @@ func TestMigratorLocking(t *testing.T) { }) } }) - assert.Equal(t, int64(1), errorNum) + assert.Equal(t, int64(1), atomic.LoadInt64(&errorNum)) } func TestDatabaseLocking(t *testing.T) { diff --git a/pkg/services/sqlstore/searchstore/builder.go b/pkg/services/sqlstore/searchstore/builder.go index 6f084b62ba7..c27451bd7e7 100644 --- a/pkg/services/sqlstore/searchstore/builder.go +++ b/pkg/services/sqlstore/searchstore/builder.go @@ -115,15 +115,31 @@ func (b *Builder) applyFilters() (ordering string) { b.params = append(b.params, whereParams...) } - if len(groups) > 0 { - b.sql.WriteString(fmt.Sprintf(" GROUP BY %s", strings.Join(groups, ", "))) - b.params = append(b.params, groupParams...) - } - if len(orders) < 1 { orders = append(orders, TitleSorter{}.OrderBy()) } + if len(groups) > 0 { + cols := make([]string, 0, len(orders)+len(groups)) + for _, o := range orders { + o := strings.TrimSuffix(o, " DESC") + o = strings.TrimSuffix(o, " ASC") + exists := false + for _, g := range groups { + if g == o { + exists = true + break + } + } + if !exists { + cols = append(cols, o) + } + } + cols = append(cols, groups...) + b.sql.WriteString(fmt.Sprintf(" GROUP BY %s", strings.Join(cols, ", "))) + b.params = append(b.params, groupParams...) + } + orderBy := fmt.Sprintf(" ORDER BY %s", strings.Join(orders, ", ")) b.sql.WriteString(orderBy) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 70c8de3de05..6436680646a 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -393,9 +393,10 @@ type Cfg struct { DefaultTheme string HomePage string - AutoAssignOrg bool - AutoAssignOrgId int - AutoAssignOrgRole string + AutoAssignOrg bool + AutoAssignOrgId int + AutoAssignOrgRole string + OAuthSkipOrgRoleUpdateSync bool // ExpressionsEnabled specifies whether expressions are enabled. ExpressionsEnabled bool @@ -1256,6 +1257,7 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) { OAuthAutoLogin = auth.Key("oauth_auto_login").MustBool(false) cfg.OAuthCookieMaxAge = auth.Key("oauth_state_cookie_max_age").MustInt(600) SignoutRedirectUrl = valueAsString(auth, "signout_redirect_url", "") + cfg.OAuthSkipOrgRoleUpdateSync = auth.Key("oauth_skip_org_role_update_sync").MustBool(false) // SigV4 SigV4AuthEnabled = auth.Key("sigv4_auth_enabled").MustBool(false) diff --git a/pkg/tsdb/cloudmonitoring/test-data/9-series-response-distribution-without-points.json b/pkg/tsdb/cloudmonitoring/test-data/9-series-response-distribution-without-points.json new file mode 100644 index 00000000000..453d3913f01 --- /dev/null +++ b/pkg/tsdb/cloudmonitoring/test-data/9-series-response-distribution-without-points.json @@ -0,0 +1,17 @@ +{ + "timeSeries": [ + { + "metric": { + "type": "loadbalancing.googleapis.com\/https\/backend_latencies" + }, + "resource": { + "type": "https_lb_rule", + "labels": { + "project_id": "grafana-prod" + } + }, + "metricKind": "DELTA", + "valueType": "DISTRIBUTION" + } + ] +} diff --git a/pkg/tsdb/cloudmonitoring/time_series_filter.go b/pkg/tsdb/cloudmonitoring/time_series_filter.go index 818565f858b..9317e29b080 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_filter.go +++ b/pkg/tsdb/cloudmonitoring/time_series_filter.go @@ -166,11 +166,10 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes if len(point.Value.DistributionValue.BucketCounts) == 0 { continue } - maxKey := 0 for i := 0; i < len(point.Value.DistributionValue.BucketCounts); i++ { value, err := strconv.ParseFloat(point.Value.DistributionValue.BucketCounts[i], 64) if err != nil { - continue + return err } if _, ok := buckets[i]; !ok { // set lower bounds @@ -193,40 +192,21 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes valueField, }, RefID: timeSeriesFilter.RefID, - } - - if maxKey < i { - maxKey = i + Meta: &data.FrameMeta{ + ExecutedQueryString: executedQueryString, + }, } } buckets[i].AppendRow(point.Interval.EndTime, value) } - for i := 0; i < maxKey; i++ { - if _, ok := buckets[i]; !ok { - bucketBound := calcBucketBound(point.Value.DistributionValue.BucketOptions, i) - additionalLabels := data.Labels{"bucket": bucketBound} - timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, []time.Time{}) - valueField := data.NewField(data.TimeSeriesValueFieldName, nil, []float64{}) - frameName := formatLegendKeys(series.Metric.Type, defaultMetricName, seriesLabels, - additionalLabels, timeSeriesFilter) - valueField.Name = frameName - valueField.Labels = seriesLabels - setDisplayNameAsFieldName(valueField) - - buckets[i] = &data.Frame{ - Name: frameName, - RefID: timeSeriesFilter.RefID, - Fields: []*data.Field{ - timeField, - valueField, - }, - } - } - } } for i := 0; i < len(buckets); i++ { + buckets[i].Meta.Custom = customFrameMeta frames = append(frames, buckets[i]) } + if len(buckets) == 0 { + frames = append(frames, frame) + } } if len(response.TimeSeries) > 0 { dl := timeSeriesFilter.buildDeepLink() diff --git a/pkg/tsdb/cloudmonitoring/time_series_filter_test.go b/pkg/tsdb/cloudmonitoring/time_series_filter_test.go index 909652a3731..72812087e51 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_filter_test.go +++ b/pkg/tsdb/cloudmonitoring/time_series_filter_test.go @@ -11,6 +11,8 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" + sdkdata "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -386,6 +388,77 @@ func TestTimeSeriesFilter(t *testing.T) { require.True(t, ok) assert.Equal(t, "114250375703598695", labels["resource.label.instance_id"]) }) + + t.Run("parseResponse successfully parses metadata for distribution valueType", func(t *testing.T) { + t.Run("exponential bounds", func(t *testing.T) { + data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json") + require.NoError(t, err) + assert.Equal(t, 1, len(data.TimeSeries)) + + res := &backend.DataResponse{} + require.NoError(t, (&cloudMonitoringTimeSeriesFilter{GroupBys: []string{"test_group_by"}}).parseResponse(res, data, "test_query")) + + require.NotNil(t, res.Frames[0].Meta) + assert.Equal(t, sdkdata.FrameMeta{ + ExecutedQueryString: "test_query", + Custom: map[string]interface{}{ + "groupBys": []string{"test_group_by"}, + "alignmentPeriod": "", + "labels": map[string]string{ + "resource.label.project_id": "grafana-prod", + "resource.type": "https_lb_rule", + }, + "perSeriesAligner": "", + }, + }, *res.Frames[0].Meta) + }) + + t.Run("explicit bounds", func(t *testing.T) { + data, err := loadTestFile("./test-data/4-series-response-distribution-explicit.json") + require.NoError(t, err) + assert.Equal(t, 1, len(data.TimeSeries)) + + res := &backend.DataResponse{} + require.NoError(t, (&cloudMonitoringTimeSeriesFilter{GroupBys: []string{"test_group_by"}}).parseResponse(res, data, "test_query")) + + require.NotNil(t, res.Frames[0].Meta) + assert.Equal(t, sdkdata.FrameMeta{ + ExecutedQueryString: "test_query", + Custom: map[string]interface{}{ + "groupBys": []string{"test_group_by"}, + "alignmentPeriod": "", + "labels": map[string]string{ + "resource.label.project_id": "grafana-demo", + "resource.type": "global", + }, + "perSeriesAligner": "", + }, + }, *res.Frames[0].Meta) + }) + + t.Run("without series points", func(t *testing.T) { + data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json") + require.NoError(t, err) + assert.Equal(t, 1, len(data.TimeSeries)) + + res := &backend.DataResponse{} + require.NoError(t, (&cloudMonitoringTimeSeriesFilter{GroupBys: []string{"test_group_by"}}).parseResponse(res, data, "test_query")) + + require.NotNil(t, res.Frames[0].Meta) + assert.Equal(t, sdkdata.FrameMeta{ + ExecutedQueryString: "test_query", + Custom: map[string]interface{}{ + "groupBys": []string{"test_group_by"}, + "alignmentPeriod": "", + "labels": map[string]string{ + "resource.label.project_id": "grafana-prod", + "resource.type": "https_lb_rule", + }, + "perSeriesAligner": "", + }, + }, *res.Frames[0].Meta) + }) + }) } func loadTestFile(path string) (cloudMonitoringResponse, error) { diff --git a/public/app/core/components/TraceToLogs/KeyValueInput.tsx b/public/app/core/components/TraceToLogs/KeyValueInput.tsx new file mode 100644 index 00000000000..40258a93874 --- /dev/null +++ b/public/app/core/components/TraceToLogs/KeyValueInput.tsx @@ -0,0 +1,110 @@ +import { css } from '@emotion/css'; +import { GrafanaTheme, KeyValue } from '@grafana/data'; +import { SegmentInput, useStyles, InlineLabel, Icon } from '@grafana/ui'; +import React from 'react'; + +const EQ_WIDTH = 3; // = 24px in inline label + +interface Props { + values: Array>; + onChange: (values: Array>) => void; + id?: string; + keyPlaceholder?: string; + valuePlaceholder?: string; +} + +const KeyValueInput = ({ + values, + onChange, + id, + keyPlaceholder = 'Key', + valuePlaceholder = 'Value (optional)', +}: Props) => { + const styles = useStyles(getStyles); + + return ( +
+ {values.length ? ( + values.map((value, idx) => ( +
+ { + onChange( + values.map((v, i) => { + if (i === idx) { + v.key = String(e); + } + return v; + }) + ); + }} + /> + + = + + { + onChange( + values.map((v, i) => { + if (i === idx) { + v.value = String(e); + } + return v; + }) + ); + }} + /> + + {idx === values.length - 1 ? ( + + ) : null} +
+ )) + ) : ( + + )} +
+ ); +}; + +export default KeyValueInput; + +const getStyles = (theme: GrafanaTheme) => ({ + wrapper: css` + display: flex; + flex-direction: column; + gap: ${theme.spacing.xs} 0; + `, + pair: css` + display: flex; + justify-content: start; + align-items: center; + `, + operator: css` + color: ${theme.palette.orange}; + `, +}); diff --git a/public/app/core/components/TraceToLogsSettings.tsx b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx similarity index 73% rename from public/app/core/components/TraceToLogsSettings.tsx rename to public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx index f2ebc087211..e8a47e9bcff 100644 --- a/public/app/core/components/TraceToLogsSettings.tsx +++ b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx @@ -3,15 +3,19 @@ import { DataSourceJsonData, DataSourcePluginOptionsEditorProps, GrafanaTheme, + KeyValue, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { DataSourcePicker } from '@grafana/runtime'; import { InlineField, InlineFieldRow, Input, TagsInput, useStyles, InlineSwitch } from '@grafana/ui'; import React from 'react'; +import KeyValueInput from './KeyValueInput'; export interface TraceToLogsOptions { datasourceUid?: string; tags?: string[]; + mappedTags?: Array>; + mapTagNamesEnabled?: boolean; spanStartTimeShift?: string; spanEndTimeShift?: string; filterByTraceID?: boolean; @@ -54,19 +58,64 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) { + {options.jsonData.tracesToLogs?.mapTagNamesEnabled ? ( + + + ({ key: tag })) ?? + [] + } + onChange={(v) => + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', { + ...options.jsonData.tracesToLogs, + mappedTags: v, + }) + } + /> + + + ) : ( + + + + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', { + ...options.jsonData.tracesToLogs, + tags: tags, + }) + } + /> + + + )} + - + ) => updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', { - datasourceUid: options.jsonData.tracesToLogs?.datasourceUid, - tags: tags, + ...options.jsonData.tracesToLogs, + mapTagNamesEnabled: event.currentTarget.checked, }) } /> @@ -156,6 +205,7 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) { /> + ( return response.blob() as any; case 'json': - return response.json(); + try { + return await response.json(); + } catch (err) { + console.warn(`${response.url} returned an invalid JSON -`, err); + return {} as unknown as T; + } case 'text': return response.text() as any; diff --git a/public/app/features/explore/LogsNavigation.tsx b/public/app/features/explore/LogsNavigation.tsx index 29007e21b2a..73dd0a540d9 100644 --- a/public/app/features/explore/LogsNavigation.tsx +++ b/public/app/features/explore/LogsNavigation.tsx @@ -81,8 +81,8 @@ function LogsNavigation({ }, [visibleRange, absoluteRange, logsSortOrder, queries, clearCache, addResultsToCache]); useEffect(() => { - return () => clearCache(); - // We can't enforce the eslint rule here because we only want to run when component unmounts. + clearCache(); + // We can't enforce the eslint rule here because we only want to run when component is mounted. // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 03210e667c9..e83c381aa35 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -20,7 +20,7 @@ import { TTraceTimeline, UIElementsContext, } from '@jaegertracing/jaeger-ui-components'; -import { TraceToLogsData } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsData } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { StoreState } from 'app/types'; diff --git a/public/app/features/explore/TraceView/createSpanLink.test.ts b/public/app/features/explore/TraceView/createSpanLink.test.ts index 9202a150b47..eb98f159813 100644 --- a/public/app/features/explore/TraceView/createSpanLink.test.ts +++ b/public/app/features/explore/TraceView/createSpanLink.test.ts @@ -2,7 +2,7 @@ import { DataSourceInstanceSettings, MutableDataFrame } from '@grafana/data'; import { setDataSourceSrv, setTemplateSrv } from '@grafana/runtime'; import { createSpanLinkFactory } from './createSpanLink'; import { TraceSpan } from '@jaegertracing/jaeger-ui-components'; -import { TraceToLogsOptions } from '../../../core/components/TraceToLogsSettings'; +import { TraceToLogsOptions } from '../../../core/components/TraceToLogs/TraceToLogsSettings'; import { LinkSrv, setLinkSrv } from '../../panel/panellinks/link_srv'; import { TemplateSrv } from '../../templating/template_srv'; @@ -141,6 +141,60 @@ describe('createSpanLinkFactory', () => { expect(linkDef!.href).toBe('testSpanId'); }); + + it('handles renamed tags', () => { + const createLink = setupSpanLinkFactory({ + mapTagNamesEnabled: true, + mappedTags: [ + { key: 'service.name', value: 'service' }, + { key: 'k8s.pod.name', value: 'pod' }, + ], + }); + expect(createLink).toBeDefined(); + const linkDef = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [ + { key: 'service.name', value: 'serviceName' }, + { key: 'k8s.pod.name', value: 'podName' }, + ], + }, + }) + ); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{service=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}' + )}` + ); + }); + + it('handles incomplete renamed tags', () => { + const createLink = setupSpanLinkFactory({ + mapTagNamesEnabled: true, + mappedTags: [ + { key: 'service.name', value: '' }, + { key: 'k8s.pod.name', value: 'pod' }, + ], + }); + expect(createLink).toBeDefined(); + const linkDef = createLink!( + createTraceSpan({ + process: { + serviceName: 'service', + tags: [ + { key: 'service.name', value: 'serviceName' }, + { key: 'k8s.pod.name', value: 'podName' }, + ], + }, + }) + ); + expect(linkDef!.href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{service.name=\\"serviceName\\", pod=\\"podName\\"}","refId":""}],"panelsState":{}}' + )}` + ); + }); }); }); diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx index 3133272d1d3..5170eb41236 100644 --- a/public/app/features/explore/TraceView/createSpanLink.tsx +++ b/public/app/features/explore/TraceView/createSpanLink.tsx @@ -3,6 +3,7 @@ import { DataLink, dateTime, Field, + KeyValue, mapInternalLinkToExplore, rangeUtil, SplitOpen, @@ -11,7 +12,7 @@ import { import { getTemplateSrv } from '@grafana/runtime'; import { Icon } from '@grafana/ui'; import { SpanLinkDef, SpanLinkFunc, TraceSpan } from '@jaegertracing/jaeger-ui-components'; -import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsOptions } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import React from 'react'; import { LokiQuery } from '../../../plugins/datasource/loki/types'; @@ -122,11 +123,22 @@ function legacyCreateSpanLinkFactory(splitOpenFn: SplitOpen, traceToLogsOptions? const defaultKeys = ['cluster', 'hostname', 'namespace', 'pod']; function getLokiQueryFromSpan(span: TraceSpan, options: TraceToLogsOptions): string { - const { tags: keys, filterByTraceID, filterBySpanID } = options; - const keysToCheck = keys?.length ? keys : defaultKeys; + const { tags: keys, filterByTraceID, filterBySpanID, mapTagNamesEnabled, mappedTags } = options; + + // In order, try to use mapped tags -> tags -> default tags + const keysToCheck = mapTagNamesEnabled && mappedTags?.length ? mappedTags : keys?.length ? keys : defaultKeys; + + // Build tag portion of query const tags = [...span.process.tags, ...span.tags].reduce((acc, tag) => { - if (keysToCheck.includes(tag.key)) { - acc.push(`${tag.key}="${tag.value}"`); + if (mapTagNamesEnabled) { + const keyValue = (keysToCheck as KeyValue[]).find((keyValue: KeyValue) => keyValue.key === tag.key); + if (keyValue) { + acc.push(`${keyValue.value ? keyValue.value : keyValue.key}="${tag.value}"`); + } + } else { + if ((keysToCheck as string[]).includes(tag.key)) { + acc.push(`${tag.key}="${tag.value}"`); + } } return acc; }, [] as string[]); diff --git a/public/app/features/plugins/admin/state/actions.ts b/public/app/features/plugins/admin/state/actions.ts index 68a47c7ebad..e23bf4b0b0e 100644 --- a/public/app/features/plugins/admin/state/actions.ts +++ b/public/app/features/plugins/admin/state/actions.ts @@ -73,6 +73,8 @@ export const install = createAsyncThunk( return { id, changes } as Update; } catch (e) { + console.error(e); + return thunkApi.rejectWithValue('Unknown error.'); } } @@ -90,6 +92,8 @@ export const uninstall = createAsyncThunk(`${STATE_PREFIX}/uninstall`, async (id changes: { isInstalled: false, installedVersion: undefined }, } as Update; } catch (e) { + console.error(e); + return thunkApi.rejectWithValue('Unknown error.'); } }); diff --git a/public/app/plugins/datasource/grafana/components/AnnotationQueryEditor.tsx b/public/app/plugins/datasource/grafana/components/AnnotationQueryEditor.tsx index 39e0d5a0821..0e956cd7e1b 100644 --- a/public/app/plugins/datasource/grafana/components/AnnotationQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana/components/AnnotationQueryEditor.tsx @@ -88,7 +88,7 @@ export default function AnnotationQueryEditor({ query, onChange }: Props) { onChange={onMaxLimitChange} /> - {type === GrafanaAnnotationType.Tags && tags && ( + {type === GrafanaAnnotationType.Tags && ( <> @@ -100,7 +100,7 @@ export default function AnnotationQueryEditor({ query, onChange }: Props) { inputId="grafana-annotations__tags" onChange={onTagsChange} tagOptions={getAnnotationTags} - tags={tags} + tags={tags ?? []} /> diff --git a/public/app/plugins/datasource/jaeger/components/ConfigEditor.tsx b/public/app/plugins/datasource/jaeger/components/ConfigEditor.tsx index 6ce9e60fd92..7ad0fe8ead0 100644 --- a/public/app/plugins/datasource/jaeger/components/ConfigEditor.tsx +++ b/public/app/plugins/datasource/jaeger/components/ConfigEditor.tsx @@ -1,7 +1,7 @@ import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourceHttpSettings } from '@grafana/ui'; import { NodeGraphSettings } from 'app/core/components/NodeGraphSettings'; -import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsSettings } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import React from 'react'; export type Props = DataSourcePluginOptionsEditorProps; diff --git a/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx b/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx index e55dd42fb75..b5bc57e135d 100644 --- a/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx +++ b/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx @@ -12,7 +12,7 @@ import { Themeable2, withTheme2, } from '@grafana/ui'; -import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsOptions } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import React from 'react'; import { LokiQueryField } from '../../loki/components/LokiQueryField'; import { LokiQuery } from '../../loki/types'; diff --git a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx index d8481a92e2a..d00d8955ae0 100644 --- a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx +++ b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx @@ -1,6 +1,6 @@ import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourceHttpSettings } from '@grafana/ui'; -import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsSettings } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import React from 'react'; import { ServiceGraphSettings } from './ServiceGraphSettings'; import { config } from '@grafana/runtime'; diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index e6b4470cd2c..4b77522a3eb 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -10,7 +10,7 @@ import { isValidGoDuration, LoadingState, } from '@grafana/data'; -import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsOptions } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import { config, BackendSrvRequest, DataSourceWithBackend, getBackendSrv } from '@grafana/runtime'; import { serializeParams } from 'app/core/utils/fetch'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; diff --git a/public/app/plugins/datasource/zipkin/ConfigEditor.tsx b/public/app/plugins/datasource/zipkin/ConfigEditor.tsx index 88b15b191d4..59a00826b8e 100644 --- a/public/app/plugins/datasource/zipkin/ConfigEditor.tsx +++ b/public/app/plugins/datasource/zipkin/ConfigEditor.tsx @@ -1,7 +1,7 @@ import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { DataSourceHttpSettings } from '@grafana/ui'; import { NodeGraphSettings } from 'app/core/components/NodeGraphSettings'; -import { TraceToLogsSettings } from 'app/core/components/TraceToLogsSettings'; +import { TraceToLogsSettings } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import React from 'react'; export type Props = DataSourcePluginOptionsEditorProps; diff --git a/scripts/build/ci-build/Dockerfile b/scripts/build/ci-build/Dockerfile index f7fc12309d2..4cd643d9f6c 100644 --- a/scripts/build/ci-build/Dockerfile +++ b/scripts/build/ci-build/Dockerfile @@ -110,7 +110,7 @@ RUN rm dockerize-linux-amd64-v${DOCKERIZE_VERSION}.tar.gz # Use old Debian (this has support into 2022) in order to ensure binary compatibility with older glibc's. FROM debian:stretch-20210208 -ENV GOVERSION=1.17.6 \ +ENV GOVERSION=1.17.7 \ PATH=/usr/local/go/bin:$PATH \ GOPATH=/go \ NODEVERSION=16.13.0-1nodesource1 \ diff --git a/scripts/drone/pipelines/docs.star b/scripts/drone/pipelines/docs.star new file mode 100644 index 00000000000..be19f140c73 --- /dev/null +++ b/scripts/drone/pipelines/docs.star @@ -0,0 +1,52 @@ +load( + 'scripts/drone/steps/lib.star', + 'initialize_step', + 'download_grabpl_step', + 'lint_frontend_step', + 'codespell_step', + 'shellcheck_step', + 'build_frontend_step', + 'test_frontend_step', + 'build_storybook_step', + 'build_frontend_docs_step', + 'build_docs_website_step', +) + +load( + 'scripts/drone/services/services.star', + 'integration_test_services', + 'ldap_service', +) + +load( + 'scripts/drone/utils/utils.star', + 'pipeline', +) + + +def docs_pipelines(edition, ver_mode, trigger): + steps = [download_grabpl_step()] + initialize_step(edition, platform='linux', ver_mode=ver_mode) + + # Insert remaining steps + steps.extend([ + build_frontend_docs_step(edition=edition), + build_docs_website_step(), + ]) + + return pipeline( + name='{}-docs'.format(ver_mode), edition=edition, trigger=trigger, services=[], steps=steps, + ) + + +def trigger_docs(): + return { + 'event': [ + 'pull_request', + ], + 'paths': { + 'include': [ + 'docs/**', + 'packages/**', + ], + }, + } diff --git a/scripts/drone/pipelines/main.star b/scripts/drone/pipelines/main.star index 583f3854160..9220d81e366 100644 --- a/scripts/drone/pipelines/main.star +++ b/scripts/drone/pipelines/main.star @@ -18,7 +18,6 @@ load( 'e2e_tests_step', 'e2e_tests_artifacts', 'build_storybook_step', - 'build_frontend_docs_step', 'copy_packages_for_docker_step', 'build_docker_images_step', 'publish_images_step', @@ -55,6 +54,11 @@ load( 'drone_change_template', ) +load( + 'scripts/drone/pipelines/docs.star', + 'docs_pipelines', +) + ver_mode = 'main' def get_steps(edition, is_downstream=False): @@ -110,7 +114,6 @@ def get_steps(edition, is_downstream=False): store_storybook_step(edition=edition, ver_mode=ver_mode), test_a11y_frontend_step(ver_mode=ver_mode, edition=edition), frontend_metrics_step(edition=edition), - build_frontend_docs_step(edition=edition), copy_packages_for_docker_step(), build_docker_images_step(edition=edition, ver_mode=ver_mode, publish=False), build_docker_images_step(edition=edition, ver_mode=ver_mode, ubuntu=True, publish=False), @@ -171,6 +174,7 @@ def main_pipelines(edition): integration_test_steps.append(benchmark_ldap_step()) pipelines = [ + docs_pipelines(edition, ver_mode, trigger), pipeline( name='main-test', edition=edition, trigger=trigger, services=[], steps=[download_grabpl_step()] + initialize_step(edition, platform='linux', ver_mode=ver_mode) + test_steps, diff --git a/scripts/drone/pipelines/pr.star b/scripts/drone/pipelines/pr.star index 2054098e899..f60e3332fc0 100644 --- a/scripts/drone/pipelines/pr.star +++ b/scripts/drone/pipelines/pr.star @@ -18,8 +18,6 @@ load( 'e2e_tests_step', 'e2e_tests_artifacts', 'build_storybook_step', - 'build_frontend_docs_step', - 'build_docs_website_step', 'copy_packages_for_docker_step', 'build_docker_images_step', 'postgres_integration_tests_step', @@ -47,6 +45,12 @@ load( 'drone_change_template', ) +load( + 'scripts/drone/pipelines/docs.star', + 'docs_pipelines', + 'trigger_docs', +) + ver_mode = 'pr' def pr_pipelines(edition): @@ -100,8 +104,6 @@ def pr_pipelines(edition): e2e_tests_artifacts(edition=edition), build_storybook_step(edition=edition, ver_mode=ver_mode), test_a11y_frontend_step(ver_mode=ver_mode, edition=edition), - build_frontend_docs_step(edition=edition), - build_docs_website_step(), copy_packages_for_docker_step(), build_docker_images_step(edition=edition, ver_mode=ver_mode, archs=['amd64',]), ]) @@ -116,7 +118,14 @@ def pr_pipelines(edition): ]) trigger = { - 'event': ['pull_request',], + 'event': [ + 'pull_request', + ], + 'paths': { + 'exclude': [ + 'docs/**', + ], + }, } return [ @@ -130,5 +139,5 @@ def pr_pipelines(edition): name='pr-integration-tests', edition=edition, trigger=trigger, services=services, steps=[download_grabpl_step()] + integration_test_steps, volumes=volumes, - ), + ), docs_pipelines(edition, ver_mode, trigger_docs()) ] diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index 19ad0ffd053..10d7a42b145 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -1,7 +1,7 @@ load('scripts/drone/vault.star', 'from_secret', 'github_token', 'pull_secret', 'drone_token', 'prerelease_bucket') -grabpl_version = 'v2.9.2' -build_image = 'grafana/build-container:1.4.9' +grabpl_version = 'v2.9.5' +build_image = 'grafana/build-container:1.5.1' publish_image = 'grafana/grafana-ci-deploy:1.3.1' deploy_docker_image = 'us.gcr.io/kubernetes-dev/drone/plugins/deploy-image' alpine_image = 'alpine:3.15' @@ -64,12 +64,20 @@ def initialize_step(edition, platform, ver_mode, is_downstream=False, install_de if ver_mode == 'release': committish = '${DRONE_TAG}' source_commit = ' ${DRONE_TAG}' + environment = { + 'GITHUB_TOKEN': from_secret(github_token), + } + token = "--github-token $${GITHUB_TOKEN}" elif ver_mode == 'release-branch': committish = '${DRONE_BRANCH}' + environment = {} + token = "" else: + environment = {} if is_downstream: source_commit = ' $${SOURCE_COMMIT}' committish = '${DRONE_COMMIT}' + token = "" steps = [ identify_runner, clone_enterprise(committish), @@ -79,14 +87,12 @@ def initialize_step(edition, platform, ver_mode, is_downstream=False, install_de 'depends_on': [ 'clone-enterprise', ], - 'environment': { - 'GITHUB_TOKEN': from_secret(github_token), - }, + 'environment': environment, 'commands': [ 'mv bin/grabpl /tmp/', 'rmdir bin', 'mv grafana-enterprise /tmp/', - '/tmp/grabpl init-enterprise --github-token $${{GITHUB_TOKEN}} /tmp/grafana-enterprise{}'.format(source_commit), + '/tmp/grabpl init-enterprise {} /tmp/grafana-enterprise{}'.format(token, source_commit), 'mv /tmp/grafana-enterprise/deployment_tools_config.json deployment_tools_config.json', 'mkdir bin', 'mv /tmp/grabpl bin/' @@ -183,7 +189,7 @@ def enterprise_downstream_step(edition): 'grafana/grafana-enterprise@main', ], 'params': [ - 'SOURCE_BUILD_NUMBER=${DRONE_BUILD_NUMBER}', + 'SOURCE_BUILD_NUMBER=${DRONE_COMMIT}', 'SOURCE_COMMIT=${DRONE_COMMIT}', ], }, @@ -421,9 +427,12 @@ def build_frontend_docs_step(edition): 'name': 'build-frontend-docs', 'image': build_image, 'depends_on': [ - 'build-frontend' + 'initialize' ], 'commands': [ + 'yarn packages:build', + 'yarn packages:docsExtract', + 'yarn packages:docsToMarkdown', './scripts/ci-reference-docs-lint.sh ci', ] } diff --git a/yarn.lock b/yarn.lock index f69b2c7924f..306363ac499 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1006,6 +1006,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-class-properties@npm:7.16.7, @babel/plugin-proposal-class-properties@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 + languageName: node + linkType: hard + "@babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.13.0, @babel/plugin-proposal-class-properties@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-proposal-class-properties@npm:7.14.5" @@ -1018,18 +1030,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" - dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 - languageName: node - linkType: hard - "@babel/plugin-proposal-class-static-block@npm:^7.15.4": version: 7.15.4 resolution: "@babel/plugin-proposal-class-static-block@npm:7.15.4" @@ -19572,6 +19572,7 @@ __metadata: resolution: "grafana@workspace:." dependencies: "@babel/core": 7.16.7 + "@babel/plugin-proposal-class-properties": 7.16.7 "@babel/plugin-proposal-nullish-coalescing-operator": 7.16.7 "@babel/plugin-proposal-object-rest-spread": 7.16.7 "@babel/plugin-proposal-optional-chaining": 7.16.7